Elixir's “Set Theoretical Type System” prototype/demo/showcase
typex.fly.dev
typex.fly.dev
In any case, the title is accurate in that it is only a prototype and it was built by Guillaume to explore how Elixir idioms map to set-theoretic types. The UX and syntax do not reflect how the type system would look in Elixir itself (if we get a type system at all).
You can find more context in Guillaume’s talk: https://m.youtube.com/watch?v=gJJH7a2J9O8
The initial research announcement: https://elixir-lang.org/blog/2022/10/05/my-future-with-elixi...
And the paper as an accurate and in-depth resource: https://www.irif.fr/_media/users/gduboc/elixir-types.pdf
This seems like really good news. It’s exciting to see where this goes.
Do you think we'll end up writing
$ integer() -> integer()
@spec inc(integer) :: integer()
def inc(i), do: i + 1
or can the new type system be entirely expressed in `@spec` (perhaps with a slightly upgraded syntax), or perhaps we'll have the choice of @spec or $ (or @spec2.0, whatever).Additionally, do you feel there is any chance/desire/need of this bleeding up into Erlang? I can only assume the work done mapping the type system onto elixir would not require as much work to then map to Erlang if there were any interest.
And yes, the semantics should map 100% cleanly to Erlang.
I was particularly impressed with the Exahustivity and redundancy in pattern matching, using the type to inform you when you have failed to fulfill the type contract across multiple expressions is _very_ clever and yet very easy to understand. This feels very much like the type of feedback I would expect from elixir when writing.
negate :: ((integer() -> integer()) and (true -> false) and (false -> true) and (a -> a) when a: not(integer() or boolean()))
harder to reason about quickly than say this
negate :: (int -> int) & (true -> false) & (false -> true) & (a -> a) when a: ~(int | boolean)
Is there a reason that syntax was chosen? Is it just for showcasing purposes or is that basically just how the language works?
Consider these two function signatures:
map :: ([a] -> [a])
map :: ([int] -> [int])
Furthermore the syntax that Elixir uses here let's you do something like
map :: (list(a) -> list(b))
list_to_other_data_structure :: (list(a) -> other_data_structure(a))
It all reminds me a bit of how Haskell does it https://medium.com/functional/haskell-basic-types-and-type-v...
I'm now curious to know if/how the above 'generics' would be expressed in TypeScript/Python/Go without a similar 'type constructor' syntax construct?
map :: forall a. ([a] -> [a])
or
map :: <a> ([a] -> [a])
If you look at any erlang api doc, you’ll see predicate-like type specs. Conveniently, it naturally supports generics by just adding “parameters” to the predicates, so a dict would have type.
It works fine, and makes things less ambiguous, as erlang “types” can contain enumerations of values, in the original spec it’s clear that `integer()` is a type and `false` is a value, in your version not so much.
You will for sure be able to drop the outer parens in your case and you _may_ be able to drop them on the function types too (to be decided). Ending up with something like this:
$ (integer -> integer) and (true -> false) and (false -> true) and (a -> a) when a: not(integer or boolean)
def negate(arg)
We will also allow intersections to be broken across multiple declarations: $ integer -> integer
$ true -> false
$ false -> true
$ a -> a when a: not(integer or boolean)
def negate(arg)Anyway coming from ocaml I don't aesthetically like this but coming from ocaml I have no grounds to talk shit about any language's aesthetic choices. The parens make sense to me when I think of them as type constructors, rather than concrete expressions of the type itself.
At least I wouldn't use it mostly because of the paranthesis and I really do hope that it's going to be optional if it were to be implemented. I do write code in typed languages on a daily basis but I do think it's overrated especially here on HN. I like that Elixir is dynamically typed and I think that it's just something that's not really in fashion at the moment but it will come around, eventually.
E.g. you could have a set which contains both colors and Joe Biden, while you couldn't have a property that both colors and Biden have, since Biden is an object but colors are properties of objects, i.e. they have different types.
In which sense would partitioning urelements lead to something like types? This sounds somewhat like many sorted logic, which is only as expressive as first-order logic, not higher-order logic / simple type theory: https://plato.stanford.edu/entries/logic-many-sorted/
> Even in type theory you have Church VS Curry style as to whether you consider terms as inheritly belonging to a type.
Not sure what the difference is here.
2. I guess the major semantic difference is that in one, you define a family of PERs for terms in a given type, giving each type separate notion of equality. In the other you define equality over all terms and types are just a predicates on terms. I honestly am not well versed enough in logic to understand what it means in that sense.
If you want a more expressive logic, use a language with dependant types. Although I wish to see more powerful type systems become more mainstream, I don't expect every language to aim for that.