I'm new in haskell. I want to learn the usage of $ and I wrote two little functions, but the second one is not working, can somebody explain me please what's wrong with that little snippet? If I understand it right $ is working like parentheses. Thanks in advance :)
myButLast::[a]->a
myButLast l = l !! (length l-2)
--not working
myButLast::[a]->a
myButLast l = l !! $ length l-2
This doesn't parse for the same reason True && || False
doesn't parse: you've used two infix operators right next to each other. What could the compiler possibly do with something like that?
You could write it this way:
myButLast l = (l!!) $ length l-2
which means, apply the function (l!!)
to length l-2
. This works because you can partially apply any Haskell function, including infix operators. But it rather defeats the point of using $
to avoid parens...
On a somewhat unrelated note: length
and !!
are code smells in Haskell. Especially !!
is both unsafe and slow, it is almost never a good idea to use it. If you really need such direct-access operations, you should use arrays/vectors (see below), but to make it at least safe you can change the signature:
myButLast :: [a] -> Maybe a
myButLast v = case length v of
l | l>=2 -> Just . (v!!) $ l-1
_ -> Nothing
Note that in this case, $
does make sense, because it allows me to parenthesize both the RHS and the LHS (the composition of the Just
and the (v!!)
functions).
Fast vector version of the same thing:
import qualified Data.Vector.Generic as VG
myButLast :: VG.Vector v a => v a -> Maybe a
myButLast v = case VG.length v of
l | l>=2 -> Just . VG.unsafeIndex v $ l-1
_ -> Nothing
If I understand it right $ is working like parentheses.
No. ($) :: (a -> b) -> a -> b
is not some special syntax, it is just an operator like (+)
, (-)
or operators you define yourself.
The reason that this works is because of the infixr 0
precedence, it thus means that this operator has the lowest precedence, and thus will group elements on the left on the right even if they contain operators, because the precedence will be higher.
($)
itself is implemented as:
infixr 0 $
($) :: (a -> b) -> a -> b
($) f x = f x
it is thus in essence simple function application, but the operator precedence let it look as if there are hidden parenthesis for the left and right operand.
The problem with your expression is that it contains now two operators after each other, indeed:
myButLast l = l !! $ length l-2
so that means that the parser got confused.
You can use operator sectioning and construct a function (l !!)
and then use the operator:
myButLast l = (l !!) $ length l-2
-- ↑ operator sectioning
But it is probably better not to use length
here in the first place: by using length you will iterate twice over the list: first to determine the list, and then to obtain the one-but-last index. This is inefficient, and likely will also result in large amounts of memory used.
You can simplify this with:
myButLast :: [a] -> a
myButLast (x:x2:xs) = go xs x x2
where go [] y _ = y
go (y2:ys) _ y = go ys y y2
myButLast _ = error "empty list"
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