# Proposal: Defining custom numeric types

Elm has built-in typeclasses like `number` and `comparable` (maybe they aren’t actually called typeclasses). For example, `number` means that the value can either be `Int` or `Float`.

I don’t think there is currently any need to be able to define custom typeclasses, but the ability to extend the built-in ones would be very useful.

Typeclasses in Elm basically define what operators are supported by a type. For example, the `number` typeclass includes all types that support `+`, `-`, and other arithmetic operators. Extending these typeclasses allows us to make custom types work with the built-in operators.

What this means is that typeclasses could be extended by simply defining the functions that implement these operators. For example, here is a type that represents a fraction:

``````module Fraction exposing (..)
type alias Fraction =
{ numerator : Int
, denominator : Int
}
``````

Currently, there is no way to make it possible to do `fraction1 + fraction2`; we have to do `Fraction.add fraction1 fraction2` (`Fraction` is a module). This is very verbose. Another problem is that we can’t pass a `Fraction` as an argument to functions like `List.sum`.

Like I said earlier, Elm typeclasses define what operators a type supports. I think it should be possible for a custom type to extend a typeclass simply by defining a function for all of the necessary operators. It would be done with something like this:

``````-- Fraction refers to a type
Fraction.compare : Fraction -> Fraction -> Order
Fraction.compare a b =
-- ...
``````

Defining the function shown above would automatically make `Fraction` a `comparable` type. So now we can do things like `fraction1 < fraction2`. More importantly, we can do `min fraction1 fraction2` without having to redefine `min`.

There should also be a syntax for explicitly saying that a type is part of a typeclass. It needs to work well with types that use type variables. Maybe something like this:

``````type alias Fraction [ comparable, number ] =
-- ...
``````

What do you think of this idea?

2 Likes

The general idea here is Abstract Data type and there have been talks about bringing something like this into Elm since forever. There is a meta-issue that captures more information (it links to other issues where discussions about various options happened). As you can see in that issue, the first discussion about this was in 2012.

2 Likes

What I am actually referring to here is constrained type variables; that’s what they are referred to here. It might also be useful to add a new constrained type variable called `list` that includes things like `Array`, `List`, and `String`. For this, it might only be necessary to define the implementation of the cons `::` operator to make a new `list` type work with things like `List.foldl` and `List.filter`.

1 Like

For extending `number` to work, we might have to merge `/` and `//` to a single `/` operator that works with any `number` as long as they have the same type.

I made a full example here

This would mostly benefit libraries that define custom numeric types. A good example is the Quantity type in elm-units. It defines many convenience functions like `max` and `abs`. If it were possible to extend `number`, and the elm-units package utilized this feature, then it would not have to redefine these convenience functions because the ones in the core libraries would work. It’s also worth mentioning that `number` is a superset of `comparable`

If you combine `/` and `//` then what’s the result of `5 / 2`? Is it `2.5` or is it `2`?

2 Likes

It could be either. It’s a matter of API design mostly.

I would suggest that returning an Int makes sense in this case.

``````(/) : Comparable t => t -> t -> t
``````

If type annotations are used to define what 5 and 2 are then this isn’t a problem. In this case, there needs to be a way to be explicit about the types of 5 and 2. Maybe if their types aren’t defined with a type annotation then it should figure it out based on whether or not there is a decimal point:

``````5 / 2 == 2
5.0 / 2.0 == 2.5
``````

@lpil what does the `Comparable t =>` mean

It’s a constraint, it says type `t` is a member of the `Comparable` type class.

Honestly, as @pdamoc pointed out, this discussion has been rehashed for nearly a decade now. It’s safe to say if there was enough interest in bringing typeclasses to elm, it would’ve happened by now.

The trend of elm’s development of the last few versions has been to take things away and reduce complexity; typeclasses add that complexity back and “my code is more verbose without them” isn’t a particularly persuasive argument.

Good point. Simplicity is what makes Elm very special and fun to use. That’s why I did not propose the ability to define custom typeclasses. In fact, now that you have mentioned reducing Elm’s complexity, I think it should only be possible to extend the `number` typeclass. I don’t see any practical use case for extending things like `appendable`, and I no longer think there should be a `list` typeclass because of how different variants of list have slightly different behavior.

Now I am only proposing the ability to define custom numeric types.

Now that I am thinking of this proposal differently, maybe the syntax for making a custom numeric type should be different.

Declaring that a type is a numeric type would look like this:

``````numeric type Fraction =
{ ... }
``````

Numeric types have to implement five functions: addition, subtraction, multiplication, division, and compare. Maybe the syntax for defining these functions could look something like this:

``````mumeric add : Fraction -> Fraction -> Fraction
fraction1 + fraction2 =
-- ...

numeric compare : Fraction -> Fraction -> Order
compare fraction2 fraction2 =
-- ...
``````

Also I no longer think `/` and `//` should be merged. `//` should be for `Int`, and `/` should be for all other `number` types, including custom ones.

For types like the `Quantity` type in elm-units, there should be a way to only have to implement a `map` function to make it a `number`. This is how `Quantity` is defined:

``````type Quantity number units
= Quantity number
``````

For this, defining `numeric map` would be the only function needed to make it a `number`:

``````numeric map : ( number -> number ) -> Quantity number u -> Quantity number u
map func ( Quantity value ) =
Quantity <| func value
``````

Actually maybe whole thing isn’t a good idea. Quantity in elm units is too different from normal numbers and having a map function could cause type safety to be lost.. If there is many practical use cases for using fractions, it should be part of the core library.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.