Generic pattern matching in Elm - elm

I'm trying to build a generic mapping function that will extract a possible inner type from a union type, apply a transform to the inner value and map it back to the outer type.
The trouble is, I need a way to distinguish if a specific value of the outer type has an inner type at all.
It would work for me if the code below actually compiled, but Elm doesn't allow constants in pattern matches. (constructor on line 4 fails to compile)
Is there any other way to accomplish this?
map : (inner -> outer) -> (inner -> inner) -> outer -> outer
map constructor func current =
case current of
constructor child ->
constructor (func child)
_ ->
current

No, unfortunately Elm doesn't have a way to express this kind of thing generically, you need to write a specific version for each custom type.

You can take a look to the elm-accessors library. With it you will need to define a Relation for every of yours type constructor (it is pretty straightforward). But then you'll be free to use a good old List.map to map over any constructor!
Here is a usage example:
type FooBar a b
= Foo a
| Bar b
items =
List.map
(over (foo << try) ((+) 1) -- affects only "Foo"s
<<
over (bar << try) (\b -> b ++ "!")) -- affects only "Bar"s
[Foo 1, Bar "a", Foo 10]
-- = [Foo 2,Bar "a!",Foo 11]
Here is a live version (Ellie).

Related

Partial application of Printf.ksprintf

I'm trying to write a version of Printf.printf that always appends a newline character after writing its formatted output. My first attempt was
# let say fmt = Printf.ksprintf print_endline fmt;;
val say : ('a, unit, string, unit) format4 -> 'a = <fun>
The type signature looks right and say works as expected. I noticed that fmt is listed twice, and thought that partial application could eliminate it. So I tried this instead:
# let say = Printf.ksprintf print_endline;;
val say : ('_weak1, unit, string, unit) format4 -> '_weak1 = <fun>
The function definition looks cleaner, but the type signature looks wrong and say no longer works as expected. For example, say doesn't type check if the format string needs a variable number of arguments: I get an error that say "is applied to too many arguments".
I can use the let say fmt = … implementation, but why doesn't partial application work?
OCaml's type-checker loses polymorphism during partial application. That is, when you partially apply a function, the resulting function is no longer polymorphic. That's why you see '_weak1 in the second type signature.
When you include the fmt argument, you help the type-checker recognize that polymorphism is still present.
This process is called "eta conversion." Removing your fmt argument is "eta reduction" and adding it back in is called "eta expansion." You may encounter that terminology when working with other functional programming languages.
This is the value restriction at work: https://ocaml.org/manual/polymorphism.html#s:weak-polymorphism . In brief, only syntactic values can be safely generalized in let-binding in presence of mutable variables in the language.
In particular,
let f = fun x -> g y x
is a syntactic value that can be generalized, whereas
let f = g y
is a computation that cannot (always) be generalized.
A example works quite well to illustrate the issue, consider:
let fake_pair x =
let store = ref None in
fun y ->
match !store with
| None ->
store := Some y;
x, y
| Some s ->
x, s
then the type of fake_pair is 'a -> 'b -> 'a * 'b.
However, once partially applied
let p = fake_pair 0
we have initialized the store mutable value, and it is important that all subsequent call to p share the same type (because they must match the stored value). Thus the type of p is '_weak1 -> int * '_weak1 where '_weak1 is a weak type variable, aka a temporary placeholder for a concrete type.

List of types from a function type

I would like to make a function that given a function type (e.g. String -> Nat -> Bool), would return a list of types corresponding to that function type (e.g. [String, Nat, Bool]). Presumably the signature of such a function would be Type -> List Type, but I am struggling to determine how it would be implemented.
I don't believe it could be done in general, because you cannot patter-match on functions. Neither can you check for the type of a function. That is not what dependent types are about. Just like in Haskell or OCaml the only thing you can actually do with a function is apply it to some argument. However, I devised some trick which might do:
myFun : {a, b : Type} -> (a -> b) -> List Type
myFun {a} {b} _ = [a, b]
Now the problem is that a -> b is the only signature that would match any arbitrary function. But, of course it does not behave the way you'd like for functions with arity higher than one:
> myFun (+)
[Integer, Integer -> Integer] : List Type
So some sort of recursive call to itself would be necessary to extract more argument types:
myFun : {a, b : Type} -> (a -> b) -> List Type
myFun {a} {b} _ = a :: myFun b
The problem here is that b is an arbitrary type, not necessarily a function type and there is no way I can figure out to dynamically check whether it is a function or not, so I suppose this is as much as you can do with Idris.
However, dynamic checking for types (at least in my opinion) is not a feature to be desired in a statically typed language. After all the whole point of static typing is to specify in advance what kind of arguments a function can handle and prevent calling functions with invalid arguments at compile time. So basically you probably don't really need it at all. If you specified what you grander goal was, someone would likely have shown you the right way of doing it.

In Elm, how to use comparable type in a tagged unions types?

I can define a tagged unions type like that:
type Msg
= Sort (Product -> Float)
But I cannot define it like:
type Msg
= Sort (Product -> comparable)
The error says:
Type Msg must declare its use of type variable comparable...
But comparable is a pre-defined type variable, right?
How do I fix this?
This question feels a little like an XY Problem. I'd like to offer a different way of thinking about passing sorting functions around in your message (with the caveat that I'm not familiar with your codebase, only the examples you've given in your question).
Adding a type parameter to Msg does seem a bit messy so let's take a step back. Sorting involves comparing two of the same types in a certain way and returning whether the first value is less than, equal to, or greater than the second. Elm already has an Order type using for comparing things which has the type constructors LT, EQ, and GT (for Less Than, EQual, and Greater Than).
Let's refactor your Msg into the following:
type Msg
= Sort (Product -> Product -> Order)
Now we don't have to add a type parameter to Msg. But how, then, do we specify which field of Product to sort by? We can use currying for that. Here's how:
Let's define another function called comparing which takes a function as its first argument and two other arguments of the same type, and return an Order value:
comparing : (a -> comparable) -> a -> a -> Order
comparing f x y =
compare (f x) (f y)
Notice the first argument is a function that looks similar to what your example was trying to attempt in the (Product -> comparable) argument of the Sort constructor. That's no coincidence. Now, by using currying, we can partially apply the comparing function with a record field getter, like .name or .price. To amend your example, the onClick handler could look like this:
onClick (Sort (comparing .name))
If you go this route, there will be more refactoring. Now that you have this comparison function, how do you use it in your update function? Let's assume your Model has a field called products which is of type List Product. In that case, we can just use the List.sortWith function to sort our list. Your update case for the Sort Msg would look something like this:
case msg of
Sort comparer ->
{ model | products = List.sortWith comparer model.products } ! []
A few closing thoughts and other notes:
This business about a comparing function comes straight from Haskell where it fulfills the same need.
Rather than defining the Sort constructor as above, I would probably abstract it out a little more since it is such a common idiom. You could define an alias for a generalized function like this, then redefine Msg as shown here:
type alias Comparer a =
a -> a -> Order
type Msg
= Sort (Comparer Product)
And to take it one step further just to illustrate how this all connects, the following two type annotations for comparing are identical:
-- this is the original example from up above
comparing : (a -> comparable) -> a -> a -> Order
-- this example substitutues the `Comparer a` alias, which may help further
-- your understanding of how it all ties together
comparing : (a -> comparable) -> Comparer a
The error you're getting is saying that comparable is an unbound variable type. You need to either fully specify it on the right hand side (e.g. Product -> Int) or specify you would like it to be polymorphic on the left hand side. Something like this:
type Msg a = Sort (Product -> a)
The question you ask about comparable is answered here: What does comparable mean in Elm?

Rationale for order of arguments to `get` function

Why are the arguments ordered the way they are in Array.get and Dict.get? The order in most map/filter functions makes sense: the function comes before the collection (or monad, more generally).
MyCollection.map : (a -> b) -> MyCollection a -> MyCollection b
This makes it easier to compose higher functions out of smaller ones before passing in a collection.
Why, however, does the index/key come before the collection in get functions?
Dict.get : comparable -> Dict comparable v -> Maybe v
It seems to make it harder to compose higher functions. If the arguments were reversed, one could write:
ids : List Int
myObjects : Dict Int a
selectObjects = List.map (Dict.get myObjects) ids
But instead, the best way I can come up with to write this is:
ids : List Int
myObjects : Dict Int a
selectObjects = List.map (\id -> (Dict.get id myObjects)) ids
Am I missing something? What is the reason for the argument order in Array.get, Dict.get, and similar?
List.map (Dict.get myObjects) ids isn't function composition.
Composition is when you have
f : X -> Y and
g : Y -> Z then you compose them
h = f >> g so that
h : X -> Z
Now, when composing functions as above, we need them to require ONLY one more parameter.
So when defining an API, the question is:
"When someone partially evaluates this function so that it can be composed, what is the last parameter they'll likely want to pass it?"
In other words, are we more likely to see code like:
getStuff >> mungeIntoDict >> (Dict.get id)
or
getStuff >> getImportantId >> (Dict.get myDict)
?
The answer to that is "it depends". The API designers took their best guess and we have flip for when it doesn't suit us.

Is there a nice way to use `->` directly as a function in Idris?

One can return a type in a function in Idris, for example
t : Type -> Type -> Type
t a b = a -> b
But the situation came up (when experimenting with writing some parsers) that I wanted to use -> to fold a list of types, ie
typeFold : List Type -> Type
typeFold = foldr1 (->)
So that typeFold [String, Int] would give String -> Int : Type. This doesn't compile though:
error: no implicit arguments allowed
here, expected: ")",
dependent type signature,
expression, name
typeFold = foldr1 (->)
^
But this works fine:
t : Type -> Type -> Type
t a b = a -> b
typeFold : List Type -> Type
typeFold = foldr1 t
Is there a better way to work with ->, and if not is it worth raising as a feature request?
The problem with using -> in this way is that it's not a type constructor but a binder, where the name bound for the domain is in scope in the range, so -> itself doesn't have a type directly. Your definition of t for example wouldn't capture a dependent type like (x : Nat) -> P x.
While it is a bit fiddly, what you're doing is the right way to do this. I'm not convinced we should make special syntax for (->) as a type constructor - partly because it really isn't one, and partly because it feels like it would lead to more confusion when it doesn't work with dependent types.
The Data.Morphisms module provides something like this, except you have to do all the wrapping/unwrapping around the Morphism "newtype".