Search code examples
haskellfunctional-programminggeneric-programmingdependent-type

Is it possible to partially apply nth parameter in Haskell?


I am curious if it is possible to write a function apply_nth that takes a function, the number of a parameter, and that parameter's value and then returns a new, partially-applied function.

The feeling I get is that this is impossible due to the type system, but I can't come up with a satisfying answer. I also can't come up with a working type signature.

If the language were more loosely-typed, I imagine the code might look like this.

apply_nth f 0 x = f x
apply_nth f n x = \a -> apply_nth (f a) (n-1) x

Any ideas?


Solution

  • Your feeling is correct, this isn't possible. Partial application changes the type of a function, and in which way depends on what parameter you apply. But if that parameter is indexed only at runtime with an extra argument, the compiler doesn't know what the type will be, and the compiler must typecheck everything. Really, you would need the result to have a dependent type, but Haskell is not a dependently-typed language.

    Now, actually, if you toss in a couple of GHC extensions and introduce a couple of weird type families, then you can actually achieve something similar to such a dependent type. But honestly, I doubt this is a good idea. What do you need this for anyway? If you're juggling functions with more than, say, 8 parameters, you're probably doing something wrong, and for easier functions you can just define 8 combinators, each of which applies a single, fixed argument-position.

    Alternatively: a similar function that's perhaps reasonable would be

    apply_nth :: ([a] -> b) -> Int -> a -> [a] -> b
    apply_nth f i a xs = f $ before ++ [a] ++ after
     where (before, after) = splitAt i xs
    

    Unlike with argument-lists, a value-list can easily be hundreds of elements long, so in this case pre-applying single elements, indexed at runtime, can make sense.


    This isn't just a safety precaution – it's necessary because types don't even exist at runtime, so the compiler needs to complete prepare all conditionals that might depend on types. This is why Haskell is safe and concise and fast and extensible, like few other languages.