What does _ mean in Elm? - elm

I'm looking at the zip example on http://elm-lang.org/examples/zip and I had a question about what exactly the _ means in Elm.
zip : List a -> List b -> List (a,b)
zip xs ys =
case (xs, ys) of
( x :: xs', y :: ys' ) ->
(x,y) :: zip xs' ys'
(_, _) ->
[]
My hunch is that it means "everything else" but does that mean any valid value? What if there is no value?

_ is used to match anything where you don't care about the value, so it's commonly used to match the "everything else" case.
In your example code (_, _) will match any tuple with 2 values in it. Note that it could also be replaced with just _ since you end up not caring about either value. A more illustrative example would be where you care about one value from the tuple but not the other, for example the implementation of fst in the core package
fst : (a,b) -> a
fst (a,_) =
a
We don't care about the second value in the tuple, so it just matches with an _ in that position.
There is no null or undefined in Elm, so you don't have to worry about there being "no value" (if something would have no value, the Maybe type is used).

Related

List split in Elm

Write a function to split a list into two lists. The length of the first part is specified by the caller.
I am new to Elm so I am not sure if my reasoning is correct. I think that I need to transform the input list in an array so I am able to slice it by the provided input number. I am struggling a bit with the syntax as well. Here is my code so far:
listSplit: List a -> Int -> List(List a)
listSplit inputList nr =
let myArray = Array.fromList inputList
in Array.slice 0 nr myArray
So I am thinking to return a list containing 2 lists(first one of the specified length), but I am stuck in the syntax. How can I fix this?
Alternative implementation:
split : Int -> List a -> (List a, List a)
split i xs =
(List.take i xs, List.drop i xs)
I'll venture a simple recursive definition, since a big part of learning functional programming is understanding recursion (which foldl is just an abstraction of):
split : Int -> List a -> (List a, List a)
split splitPoint inputList =
splitHelper splitPoint inputList []
{- We use a typical trick here, where we define a helper function
that requires some additional arguments. -}
splitHelper : Int -> List a -> List a -> (List a, List a)
splitHelper splitPoint inputList leftSplitList =
case inputList of
[] ->
-- This is a base case, we end here if we ran out of elements
(List.reverse leftSplitList, [])
head :: tail ->
if splitPoint > 0 then
-- This is the recursive case
-- Note the typical trick here: we are shuffling elements
-- from the input list and putting them onto the
-- leftSplitList.
-- This will reverse the list, so we need to reverse it back
-- in the base cases
splitHelper (splitPoint - 1) tail (head :: leftSplitList)
else
-- here we got to the split point,
-- so the rest of the list is the output
(List.reverse leftSplitList, inputList)
Use List.foldl
split : Int -> List a -> (List a, List a)
split i xs =
let
f : a -> (List a, List a) -> (List a, List a)
f x (p, q) =
if List.length p >= i then
(p, q++[x])
else
(p++[x], q)
in
List.foldl f ([], []) xs
When list p reaches the desired length, append element x to the second list q.
Append element x to list p otherwise.
Normally in Elm, you use List for a sequence of values. Array is used specifically for fast indexing access.
When dealing with lists in functional programming, try to think in terms of map, filter, and fold. They should be all you need.
To return a pair of something (e.g. two lists), use tuple. Elm supports tuples of up to three elements.
Additionally, there is a function splitAt in the List.Extra package that does exactly the same thing, although it is better to roll your own for the purpose of learning.

Why is filter based on dependent pair?

In the Idris Tutorial a function for filtering vectors is based on dependent pairs.
filter : (a -> Bool) -> Vect n a -> (p ** Vect p a)
filter f [] = (_ ** [])
filter f (x :: xs) with (filter f xs )
| (_ ** xs') = if (f x) then (_ ** x :: xs') else (_ ** xs')
But why is it necessary to put this in terms of a dependent pair instead of something more direct such as?
filter' : (a -> Bool) -> Vect n a -> Vect p a
In both cases the type of p must be determined, but in my supposed alternative the redundancy of listing p twice is eliminated.
My naive attempts at implementing filter' failed, so I was wondering is there a fundamental reason that it can't be implemented? Or can filter' be implemented, and perhaps filter was just a poor example to showcase dependent pairs in Idris? But if that is the case then in what situations would dependent pairs be useful?
Thanks!
The difference between filter and filter' is between existential and universal quantification. If (a -> Bool) -> Vect n a -> Vect p a was the correct type for filter, that would mean filter returns a Vector of length p and the caller can specify what p should be.
Kim Stebel's answer is right on the money. Let me just note that this was already discussed on the Idris mailing list back in 2012 (!!):
filter for vector, a question - Idris Programming Language
What raichoo posted there can help clarifying it I think; the real signature of your filter' is
filter' : {p : Nat} -> {n: Nat} -> {a: Type} -> (a -> Bool) -> Vect a n -> Vect a p
from which it should be obvious that this is not what filter should (or even could) do; p actually depends on the predicate and the vector you are filtering, and you can (actually need to) express this using a dependent pair. Note that in the pair (p ** Vect p a), p (and thus Vect p a) implicitly depends on the (unnamed) predicate and vector appearing before it in its signature.
Expanding on this, why a dependent pair? You want to return a vector, but there's no "Vector with unknown length" type; you need a length value for obtaining a Vector type. But then you can just think "OK, I will return a Nat together with a vector with that length". The type of this pair is, unsurprisingly, an example of a dependent pair. In more detail, a dependent pair DPair a P is a type built out of
A type a
A function P: a -> Type
A value of that type DPair a P is a pair of values
x: a
y: P a
At this point I think that is just syntax what might be misleading you. The type p ** Vect p a is DPair Nat (\p => Vect p a); p there is not a parameter for filter or anything like it. All this can be a bit confusing at first; if so, maybe it helps thinking of p ** Vect p a as a substitute for the "Vector with unknown length" type.
Not an answer, but additional context
Idris 1 documentation - https://docs.idris-lang.org/en/latest/tutorial/typesfuns.html#dependent-pairs
Idris 2 documentation - https://idris2.readthedocs.io/en/latest/tutorial/typesfuns.html?highlight=dependent#dependent-pairs
In Idris 2 the dependent pair defined here
and is similar to Exists and Subset but BOTH of it's values are NOT erased at runtime

Understanding 'impossible'

Type-Driven Development with Idris presents:
twoPlusTwoNotFive : 2 + 2 = 5 -> Void
twoPlusTwoNotFive Refl impossible
Is the above a function or value? If it's the former, then why is there no variable arguments, e.g.
add1 : Int -> Int
add1 x = x + 1
In particular, I'm confused at the lack of = in twoPlusTwoNotFive.
impossible calls out combinations of arguments which are, well, impossible. Idris absolves you of the responsibility to provide a right-hand side when a case is impossible.
In this instance, we're writing a function of type (2 + 2 = 5) -> Void. Void is a type with no values, so if we succeed in implementing such a function we should expect that all of its cases will turn out to be impossible. Now, = has only one constructor (Refl : x = x), and it can't be used here because it requires ='s arguments to be definitionally equal - they have to be the same x. So, naturally, it's impossible. There's no way anyone could successfully call this function at runtime, and we're saved from having to prove something that isn't true, which would have been quite a big ask.
Here's another example: you can't index into an empty vector. Scrutinising the Vect and finding it to be [] tells us that n ~ Z; since Fin n is the type of natural numbers less than n there's no value a caller could use to fill in the second argument.
at : Vect n a -> Fin n -> a
at [] FZ impossible
at [] (FS i) impossible
at (x::xs) FZ = x
at (x::xs) (FS i) = at xs i
Much of the time you're allowed to omit impossible cases altogether.
I slightly prefer Agda's notation for the same concept, which uses the symbol () to explicitly pinpoint which bit of the input expression is impossible.
twoPlusTwoNotFive : (2 + 2 ≡ 5) -> ⊥
twoPlusTwoNotFive () -- again, no RHS
at : forall {n}{A : Set} -> Vec A n -> Fin n -> A
at [] ()
at (x ∷ xs) zero = x
at (x ∷ xs) (suc i) = at xs i
I like it because sometimes you only learn that a case is impossible after doing some further pattern matching on the arguments; when the impossible thing is buried several layers down it's nice to have a visual aid to help you spot where it was.

Why does mis-spelling true/false not raise a compiler error in the online Elm editor (as opposed to elm-repl)?

The following Elm program is supposed to print 10 if the mouse button is pressed and 20 if it is not pressed, but it always prints 20 (when running it at http://elm-lang.org/try) :
import Mouse
import Text (asText)
import Signal (map)
nextVal : Bool -> Int
nextVal down =
case down of
true -> 10
false -> 20
main = map asText (map nextVal Mouse.isDown)
The cause for this behaviour is a simple mis-spelling - if you replace true with True and false with False, everything works as expected.
But why don't I get a compiler error for this? I'd have expected something similar to the error message I get from the elm-repl: Could not find variable 'true'
UPDATE
In fact (as hinted at in the answer by #Apanatshka), this code also works in the REPL, so Elm behaves consistently.
I'd have to look into why elm-repl gives that error.
When I try this code on elm-lang.org/try it always gives 10. The code you've provided is valid Elm code. When you write a lowercase name in a case-of pattern, that name is considered a pattern variable. It will match anything and bind what it matches to that name. So your nextVal function will try to match a boolean with the given patterns, in the order given. It starts with the first pattern, which is a single pattern variable, therefore it always matches. There it stops searching, because the pattern matches, therefore 10 is always returned.
Perhaps you would prefer to use an if-then-else?
nextVal down =
if down
then 10
else 20
One last thing: When you're just matching an enumeration like Bool it probably doesn't seem useful to have these pattern variables. Just to show that it can be useful, I wrote a little example with a singly linked list:
type SLList a =
Cons a (SLList a)
| Nil
-- a list of 1,2,3 would look like: Cons 1 (Cons 2 (Cons 3 Nil))
removeSurroundingElements : SLList a -> SLList a
removeSurroundingElements l =
case l of
Nil -> Nil
Cons _ Nil -> l -- `_` means ignore
Cons _ (Cons _ Nil) -> l
Cons e1 (Cons e2 (Cons e3 tail)) ->
Cons e2 (removeSurroundingElements tail)
-- removeSurroundingElements (Cons 1 (Cons 2 (Cons 3 Nil))) == Cons 2 Nil
-- removeSurroundingElements (Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 (Cons 6 Nil))))))
-- == Cons 2 (Cons 5 Nil)

Accumulator in Haskell

I have this function:
map(\x -> if (isLetter x) then (update exp (Literal x) "") else Epsilon) "a+b+c+" where exp = Epsilon
and i want for each step of the map function my variable exp to not be Epsilon but to remain the same as the step before and I also want to keep the list of intermediate results. Can someone help me ?
Since you want to keep the intermediate results, the direct translation from what I gather to be the intent is
scanl (\exp x -> if (isLetter x) then update exp (Literal x) "" else Epsilon) Epsilon "a+b+c+"
The type of scanl is
Prelude> :t scanl
scanl :: (a -> b -> a) -> a -> [b] -> [a]
the first argument is a function to combine the current "state" and the next list element, the second is the initial "state". So the above updates the "state" using the function you supplied, adjusted to take two arguments.
I'm not sure that you really want to reset the accumulator to Epsilon for each non-letter, if not, you'd need to change the combining function, maybe
\exp x -> if (isLetter x) then update exp (Literal x) "" else exp
to keep the old value of the accumulator for non-letters.