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

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?

Related

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.

Elm Language What do the Multiple Types in a row (without the arrow) in a signature mean?

In the Elm language, I'm having a hard time explaining my question...
In these snippets in elm:
I understand the signature in something like
update : Msg -> Model -> Model
where the parameters / output is separated by arrows, but how do I read / grok things like:
Sub Msg
Program Never Model Msg
In:
main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
In a type signature, parameter types are separated by ->, with the last type being the return value.
If there are no -> symbols, then it means it is a value of that type. In your main example, the type of main is Program Never Model Msg. It has no arrows, so it takes no parameters.
Now, each parameter and the return value in the type annotation may have several things separated by spaces, as in your main example. The leftmost is the type, followed by type parameters separated by spaces.
Program Never Model Msg
| | | |
| ------|-----
type type parameters
A type parameter is similar to Generics in a language like C#. The equivalent syntax in C# would be:
void Program<Never, Model, Msg>()
C# doesn't directly correlate because it has a different way of constraining generic type parameters, but the general idea holds.
The Elm guide doesn't currently have a great deal of info, but here is the section talking about types.
Sub Msg, List Int, Program Never Model Msg
Sub, List and Program are type constructors. You can think about them as functions that take a type and return another type.
By themselves, Sub, List and Program are not complete type. They are like a puzzle missing a piece. When one adds the missing piece, the puzzle is complete.
I usually read them in my head using the word of as in a List of Ints, a Program of Never, Model and Msg.

Choosing a typeclass on construction of a data type

I have a a data type in idris:
data L3 = Rejected | Unproven | Proven
which I verified to be a ring with unity, a lattice, a group and some other properties too.
Now I want to create an object, which preserves the expressions of the statements I inject in it. I started out with four categories to represent all the operations, so I get a nice syntax tree out of it. Eg:
Om [Proven, Unproven, Op [Proven, Oj [Unproven, Proven]]
This is not the real representation, I stripped some of the needed ugly parts, but it gives an idea of what I try to achieve, the above is equivalent to:
meet Proven (meet Unproven (Proven <+> (join Unproven Proven)))
I recognized I could join the data types together into one. To get there I created a function, which will pick the correct class instance:
%case data Operator = Join | Meet | Plus | Mult
classChoice : (x: Operator) -> (Type -> Type)
classChoice Join = VerifiedJoinSemilattice
classChoice Meet = VerifiedMeetSemilattice
classChoice Plus = VerifiedGroup
classChoice Mult = VerifiedRing
So I could assure that anything in the type represents one of those four operations:
%elim data LogicSyntacticalCategory : classChoice op a => (op : Operator) -> (a : Type) -> Type where
LSCEmpty : LogicSyntacticalCategory op a
It will complain with:
When elaborating type of logicCategory.LSCEmpty:
Can't resolve type class classChoice op ty
Now my question: How can I assure that the objects in my data type are verified and join the four separate data types into one. I really would like to ensure this is true during construction. I can understand it has difficulties resolving the type class now, but I want Idris to ensure it can do it later during construction. How can I do this?
Code isn't really needed, I am quite happy with a direction of thought.
Two minor problems first: ... -> a -> ... should be ... -> (a : Type) -> ..., and syntactical is how it's written.
Warning: I'm working with Idris 0.9.18 and don't know how to write Elab proofs yet.
Repository: https://github.com/runKleisli/idris-classdata
In normal functions with these same type signatures, you have the opportunity to assist the type class resolution with tactics while defining the functions. But with the data type and its constructors, you only have the opportunity to declare them, so you have no such opportunity to assist in resolution. It would appear such guided resolution was needed here.
It appears that classChoice op a needs an instance proved before the LogicSyntacticleCategory op a in the definition of LSCEmpty makes sense, and that it did not get this instance. Class constraints in the data type's type like this are usually automatically introduced into the context of the constructor, like an implicit argument, but this seems to have failed here, and an instance is assumed for a different type than the one required. That instance assumed for the constructor not satisfying the goal introduced by declaring a LogicSyntacticleCategory op a seems to be the error. In one of the examples in the repository, these unexpectedly mismatched goal and assumption seem able to automatically pair, but not under the circumstances of the data type & constructor declarations. I can't figure out the exact problem, but it seems not to apply to plain function declarations with the same conditions on the type signature.
A couple solutions are given in the repository, but the easiest one is to replace the constraint argument, saying an instance of classChoice op a is required, with an implicit argument of type classChoice op a, and to evaluate LogicSyntacticleCategory like
feat : Type
feat = ?feat'
feat' = proof
exact (LogicSyntacticleCategory Mult ZZ {P=%instance})
If you are set on having a constraint argument in your main interface to the data type, you can wrap the definition of LogicSyntacticleCategory : (op : Operator) -> (a : Type) -> {p : classChoice op a} -> Type with the function
logicSyntacticleCategory : classChoice op a => (op : Operator) -> (a : Type) -> Type
logicSyntacticleCategory = ?mkLogical
mkLogical = proof
intros
exact (LogicSyntacticleCategory op a {P=constrarg})
and when you want to make a type of the form LogicSyntacticleCategory op a, evaluate like before, but with
feat' = proof
exact (logicSyntacticleCategory Mult ZZ)
exact Mult
exact ZZ
compute
exact inst -- for the named instance (inst) of (classChoice Mult ZZ)
where the last line is dropped for anonymous instances.

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".

SML/NJ: How to use HashTable?

I really want to create a HashTable in SML, it seems there already is a structure for this in SML/NJ.
The question is, how do I use it? I've not fully understood how to use structures in SML, and some of the very basic examples in the book I read gives me errors I don't even know how to correct, so using the HashTable structure might be an easy thing, but I wouldn't know. If someone could explain this, then that'd be wonderful too!
I'm thinking it's something like this:
val ht : string * int HashTable.hash_table = HashTable.mkTable();
???
The signature of the mkTable value is:
val mkTable : (('a -> word) * (('a * 'a) -> bool)) -> (int * exn)
-> ('a,'b) hash_table
(* Given a hashing function and an equality predicate, create a new table;
* the int is a size hint and the exception is to be raised by find.
*)
Therefore, you would have to do something like:
val ht : (string, int) HashTable.hash_table =
HashTable.mkTable (HashString.hashString, op=) (42, Fail "not found")
I assume the idea is to create a table mapping strings to integers. Then you want to write its type as (string, int) hash_table (the type hash_table is a type with two parameters, which are written like that in ML).
But you also need a hash function hash : string -> word and an equality function eq : string * string -> bool over strings to provide to mkTable. For the latter, you can simply use op=, for the former you can use HashString.hashString from the respective module.
So,
val ht : (string, int) HashTable.hash_table = HashTable.mkTable(HashString.hashString, op=)(17, Domain)
should work.
I should note, however, that hash tables tend to be vastly overused, and more often than not they are the wrong data structure. This is especially true in functional programming, since they are a stateful data structure. Usually you are better off (and potentially even more efficient) using some tree-based map, e.g., the RedBlackMapFn from the SML/NJ library.