If using List.isEmpty
doesn’t work in your example (because of a compiler error) — what’s it useful for?
Here’s an example:
viewCart products =
if List.isEmpty products then
div [] [ text "Your cart is empty" ]
else
ul [] (List.map viewProduct products)
Surely you don’t need to case
on every function that uses Maybe
?
There are lots of helper functions like Maybe.map
, Maybe.andThen
and Maybe.withDefault
which you’ll use often, but a good old case
on a Maybe
is still a common pattern.
it would seem to me that first :: rest
uses List.head
and List.tail
It’s the other way around: List.head and List.tail source code.
First checks that the list argument is a List type
Since Elm is statically typed, it doesn’t need to do a runtime check if it’s a list. It knows it’s a list, and the compiled code assumes that.
Then returns Just [1]
and Just [2, 3, 4]
(given a List number).
There is no Maybe
(which means no Just
) involved when pattern matching on a list.
The operator ::
is indeed weird. I thought (::)
was only for adding an item to a list? The equivalent of (cons ...)
in lisp.
The idea with the pattern matching syntax is that it looks a pretty much like the regular syntax for building that thing. For example, you can have myNumber = 123
and pattern match with:
case myNumber of
123 -> "It's 123!"
-- ^^^ this looks like the regular syntax for building a number
_ -> "It's something else"
And you can have:
anEmptyList = []
oneItem = [1]
twoItems = [1, 2]
And pattern match:
case someList of
[] -> "It's an empty list"
[imSingle] -> "It's a list with one item, which is " ++ String.fromInt imSingle
[first, second] -> "It's a list with two items, which are " ++ String.fromInt first ++ " and " ++ String.fromInt second
_ -> "It's a list with more than two items"
Then we have the ::
a.k.a. “cons” operator, which lets you construct lists in a more verbose way:
anEmptyList = []
oneItem = 1 :: []
twoItems = 1 :: 2 :: []
Elm has pattern matching syntax using ::
too, for symmetry (and if you want to match on a list of unknown length this is the only way of doing it):
case someList of
[] -> "It's an empty list"
first :: [] -> "It's a list with one item, which is " ++ String.fromInt first
first :: second :: [] -> "It's a list with two items, which are " ++ String.fromInt first ++ " and " ++ String.fromInt second
_ -> "It's a list with more than two items"
That’s the only operator in Elm that has a pattern matching syntax, as far as I know. So this is not a thing:
case 5 of
1 + 4 -> "It's 5"
-- ^^^^^ a hypothetical language feature
_ -> "It's something else"
I’ve already tried changing those variable names (like x :: y
) to the left of the second ->
branch, so they’re not special names right?
Correct, you can name them whatever you want.
is (1) saying “last in list”
No, it’s saying “the only item in a list of length 1”.
(2) equivalent to your version head :: tail
?
Yes, parentheses, spaces and choice of names don’t matter.
trying to do [] :: [1, 2, 3, 4]
throws an error
That’s because the left side of ::
is one item of the list, and the right side is the rest of the list. But you have a list on the left. So 0 :: [1, 2, 3, 4]
would be valid (it evaluates to [0, 1, 2, 3, 4]
), and [] :: [[1], [2], [3], [4]]
would be valid (the list on the right contains lists, and the left side is one such list, and it evaluates to [[], [1], [2], [3], [4]]
).
what can go in between case ... of
Anything. However, some things are so called “opaque types” which you can’t do anything with. And if you put a function between case
and of
, there isn’t any meaningful way to pattern match on it. However, there’s nothing stopping you from doing:
case List.map of
_ -> 1
(It evaluates to 1.)
what goes before the ->
? But seems like you can’t treat it like an if
statement with a conditional like this: List.isEmpty list -> "Empty"
The thing before ->
is special pattern matching syntax. You can’t put any piece of code there. There’s pattern matching syntax for booleans, numbers, strings, lists, tuples, records, custom types, and a few things more, but not much.
What’s the benefit of using a Day
type over a Tuple
type? How do you grab the index to retrieve the string?
None in this case I think, that’s just me trying to produce some Elm code that looked really similar to your Racket code. Didn’t realize your Racket code had tuples.
Is there a good link to using the right data types you could forward me? A set of days is a finite list so I’m guessing there’s a better way.
The elm/time
package has a Weekday
type: Time - time 1.0.0
rather than the “Beginning Elm” one that uses a case
expression for every index. […] I was then hoping to create a simple getDay index
function to find the corresponding day (the “string” part of the tuple).
The “Beginning Elm” way is the idiomatic way, I’d say.
The following code is very readable and performant:
import Time
getDay : Int -> Maybe Time.Weekday
getDay index =
case index of
0 -> Just Time.Mon
1 -> Just Time.Tue
2 -> Just Time.Wed
3 -> Just Time.Thu
4 -> Just Time.Fri
5 -> Just Time.Sat
6 -> Just Time.Sun
_ -> Nothing
I hope I managed to answer most of your questions somewhat well!
Btw, do you know any other programming languages? Sometimes I find it nice to explain Elm features in terms of another programming language, but I don’t know Racket so I can’t do that here.