For example in the Elm basics docs:
(/) : Float -> Float -> Float
This is saying that /
is a function which takes a float, and another float and returns a float.
Why wouldn't it be like:
(/) : (Float, Float) -> Float
For example, which seems to be more intiutive.
Is there a special reason for this? This is also how types are annotated in Haskel also.
https://package.elm-lang.org/packages/elm/core/latest/Basics
EDIT: This has been answered, but I also found this in the documentation for Elm: https://guide.elm-lang.org/appendix/function_types.html
Technically Elm does not have functions of more than one argument. The type Float -> Float -> Float
(which is the same as Float -> (Float -> Float)
because ->
is right-associcative) represents a function that takes a Float
and returns another function that takes another float. Then a call like (/) a b
(which is the same as ((/) a) b
because function application is left-associative) first applies the function (/)
to a
and then applies the resulting function to b
.
Note that you can also just apply /
to a single argument without applying the result to a second argument right away: For example inv = (/) 1
would define an inv
function that acts the same as inv x = 1 / x
.
(Float, Float) -> Float
would be the type of a function that takes a tuple containing two floats. You could define a function with that type like this:
f: (Float, Float) -> Float
f (x,y) = ...
And then you could call it as f (arg1, arg2)
where arg1
and arg2
are floats or f tup
where tup
is a tuple of two floats.
In functional programming it's easier to use version which returns other functions so when you see (/) : Float -> Float -> Float
it means that you can pass one argument and get the function Float -> Float
back. So effectively there is no difference in the result (assuming that you pass both arguments).
This technique of going from tuple version (uncurried) to functional version (curried) is called currying and is widely used and will become intuitive after practice. I'm not familiar with elm
but in Haskell you can easily go from curried version and back:
f :: a -> (b -> c) -- (which can also be written as f :: a -> b -> c )
g :: (a, b) -> c
f = curry g -- transform argument from tuple to functions
g = uncurry f -- transform function arguments to tuple
So if you apply the function f
to only one argument you get back a function which can be re-used. Check the Haskell wiki for more details. In contrary with function which expect tuple the re-usability will be limited. Check also the Partial application section of the wiki to understand why it's important.
add2 :: Integer -> Integer -> Integer
add2 a b = a + b
add5 :: Integer -> Integer
add5 b = add2 5 b -- add2 is re-used
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With