-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better type signatures for methods #34
Comments
Also maybe we should pull out
which would read like this: Module
I feel like I reinventing type classes though 😄 |
This is kind of what I was talking about in that comment from the PR! Typeclass syntax conflates dictionaries with automated lookup. To be more concrete, Haskell's compiler translates
into
where In OCaml you can't quite write this, but the idea would be
where the all caps names reference module signatures which say that whatever dictionary/module gets passed it must satisfy the type |
Btw, this starts to look like module signatures, so we won't have that confusion between method signature and module signature if we go this way, because we will have only module signatures. I think we have 3 options:
I like the first so far, Haskell's syntax seems to be familiar to the largest group of potential readers, and seems like it won't require too many modifications. |
If you are looking for ideas on how to precisely specify the concepts with a module based language, then I'd suggest 1ML. Unlike OCaml, 1ML is expressive enough for the job. |
I agree @polytypic, 1ML is a good idea. The unification is type theoretically tricky but a good simple model for an untyped language. |
Not sure if I understand what that means :) As I see it, we need something like OCaml's module signature language. Basically we want to define "algebra" as a module signature + laws. So to my understanding OCaml's signatures are expressive enough for our needs. Although I don't know anything about 1ML yet, and I just started learning OCaml. Anything in particular I should look at in 1ML if I'll be reading about it? |
A clarification, just in case: whenever I've mentioned "module" before in this issue I've meant dictionary with functions (JS object with functions). Not a ES6 module or anything. |
The basic idea is that in OCaml the module signature language and module definition language (types and dictionaries) are segregated from the type signature language and the value definition language. This is for technical reasons: the separation helps prevent certain pathological cases with higher-order types which make type foundations difficult and inference impossible. 1ML is a re-envisioning of ML that removes this segregation and lets you see modules as normal dictionaries and module signatures as normal types. It is in that way simpler, but suffers from a more complex type theoretic background and lesser type inference results. Since the types you're working with are not machine-checked there's a good chance that neither of the above issues will ever be encountered. This make 1ML a pretty good influence. OTOH, there are a ton of OCaml and even SML resources. There's pretty much a handful of papers alone on 1ML. |
Here is what I came up with. These are signatures for modules. A module to support an algebra will have to match corresponding signature and obey laws. I think this is pretty intuitive for people who're familiar with Flow/TypeScript syntax. Setoid<T> {
equals: (T, T) => boolean
}
Semigroup<T> {
concat: (T, T) => T
}
Monoid<T> extends Semigroup<T> {
empty: () => T
}
Functor<T> {
map: (a => b, T<a>) => T<b>
}
Bifunctor<T> extends Functor<T<*, _>> {
map: (c => d, T<a, c>) => T<a, d>,
bimap: (a => b, c => d, T<a, c>) => T<b, d>
}
Contravariant<T> {
contramap: (a => b, T<b>) => T<a>
}
Profunctor extends Functor<T<*, _>> {
map: (c => d, T<a, c>) => T<a, d>,
promap: (a => b, c => d, T<b, c>) => T<a, d>
}
Apply<T> extends Functor<T> {
ap: (T<a => b>, T<a>) => T<b>
}
Applicative<T> extends Apply<T> {
of: (a) => T<a>
}
Alt<T> extends Functor<T> {
alt: (T<a>, T<a>) => T<a>
}
Plus<T> extends Alt<T> {
zero: () => T<a>
}
Alternative<T> extends Applicative<T>, Plus<T> {
}
Chain<T> extends Apply<T> {
chain: (a => T<b>, T<a>) => T<b>
}
ChainRec<T> extends Chain<T> {
chainRec: ((a => c, b => c, a) => T<c>, a) => T<b>
}
Monad<T> extends Applicative<T>, Chain<T> {
}
Foldable<T> {
reduce: ((a, b) => a, a, T<b>) => a
}
Extend<T> {
extend: (T<a> => b, T<a>) => T<b>
}
Comonad<T> extends Functor<T>, Extend<T> {
extract: (T<a>) => a
}
Traversable<T> extends Functor<T>, Foldable<T> {
traverse: (Applicative<U>, a => U<b>, T<a>) => U<T<b>>
} One tricky part is when an algebra that works with types with two holes extends an algebra that works with types with only one hole. See Also this syntax should allow us to describe algebras that are related to more than one type (see #32 (comment)) . |
While thinking about #32, I've realized that we have another flaw in type signatures that we use. For example:
Here we say that
t
must be aTraversable
andf
must be anApplicative
, but that doesn't make sense. We can't require from a type to support an algebra, we can require that only from a module (I use terminology from this comment).I think we could use something like this instead:
which reads as:
T
that supportsTraversable
algebra and works with typet
,A
that supportsApplicative
algebra and works with typef
,T
has a methodtraverse
,A
, a functiona → f b
, and a valuet a
,f (t b)
.Somewhat related: #31
The text was updated successfully, but these errors were encountered: