I was thinking about keyword arguments (like in Python) and why Haskell doesn’t support them.
It seems that keyword arguments could improve readability at the call site, especially when multiple parameters have similar types. However, Haskell relies heavily on currying and partial application, where functions are applied one argument at a time.
So my question is:
Would adding keyword arguments conflict with currying in Haskell?
For example, how would partial application work if arguments could be passed out of order or by name?
Is this one of the main reasons Haskell avoids keyword arguments, or are there deeper design considerations?
Also, are records essentially the intended alternative here?
Arguments of same type can be disambiguated with phantom types trick.
{-# LANGUAGE TypeApplications #-}
import Data.Tagged
data A
data B
f :: Tagged A Int -> Tagged B Int -> Int
f (Tagged a) (Tagged b) = a + b
withF :: Int
withF = f (pure @A 1) (pure @B 2)
I think I would restrict it to functions having either keyword arguments or positional arguments, so forbidding a combination of the two. In that case you could just consider a function with keyword arguments to be a function from a record type. For example my imagined syntax would be:
magnitude :: {x :: Int, y :: Int} -> Double
magnitude {x = x, y = y} = sqrt (fromIntegral (x * x + y * y))
That could be desugared into:
data MagnitudeArg = MkMagnitudeArg { x :: Int, y :: Int }
magnitude :: MagnitudeArg -> Double
magnitude MkMagnitudeArg {x = x, y = y} = sqrt (fromIntegral (x * x + y * y))
In the Purescript Haskell dialect you can already write such functions using anonymous records:
magnitude :: {x :: Number, y :: Number} -> Number
magnitude {x, y} = sqrt (x * x + y * y)
And there is a proposal to add support for it to GHC (although it is a lot more ambitious):
But then writing magnitude { x = .., y = .. } would be ambiguous due to record update syntax, so deprecating record update syntax could fix that as well.
I feel like GHC should be able to tell that magnitude is a function with named arguments and treat it differently based on that. Though that can indeed be confusing.
There’d probably be some issues with type resolution I’m not aware of, but to me the main confusion would come from there effectively being two different types of record syntax. The one for partial record update is almost never used over all arguments (because it’s partial), whereas one for providing function arguments has to provide all arguments (because syntactically they can be provided in any order, so leaving any of them out to be provided positionally would be confusing/ambiguous).