Skip to content

Instantly share code, notes, and snippets.

@ahammel
Last active April 15, 2017 03:46
Show Gist options
  • Select an option

  • Save ahammel/bea2dc125a422c6789c1d7f4b662ef9c to your computer and use it in GitHub Desktop.

Select an option

Save ahammel/bea2dc125a422c6789c1d7f4b662ef9c to your computer and use it in GitHub Desktop.
Alpaca type classes (sort of)
module semigroup
export_type semigroup
export concat
{- Semigroup: anything with an associative operation
Instead of a type class, we define a record type which specifies the
necessary fields.
Inspired by the [object algebra] approach and, more directly, the
[scrap your typeclasses] proposal.
[object alebra approach]: https://www.cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf
[scrap your typeclasses]: http://www.haskellforall.com/2012/05/scrap-your-type-classes.html
-}
type semigroup 'a = { append: fn 'a 'a -> 'a }
{- Reduce a list by appending, starting with a default value.
Polymorphic functions take an instance of the `semigroup`.
In this particular case it would be better if instances of the semigroup
class could override the default implementation. (The semigroup instance of
`option`, for instance, can short-circuit evalute.) I'm not sure how to
implement that, however.
-}
let concat iSemigroup x [] = x
let concat iSemigroup x y::ys =
match iSemigroup with
{append=f} -> concat iSemigroup (f x y) ys
module monoid
export_type monoid
export concat
{- Class for types with an associate operation and an identity element.
Of note: `monoid` is an implicit sub-type of `semigroup`. Any `monoid`
instance counts as a `semigroup` instance as well because it has an
`append` field with the right type (yay row polymorphism!).
It might be nice to be able to make the type hierarchy explicit with a
syntax like this:
```type monoid 'a = {empty: 'a | semigroup 'a }```
-}
type monoid 'a = {empty: 'a, append: fn 'a 'a -> 'a}
let concat iMonoid xs =
match iMonoid with
{empty=e} -> semigroup.concat iMonoid e xs
-- ^ taking advantage of that row-polymorphism
module list
export append
let append [] ys = ys
let append (x::xs) ys = x :: (append xs ys)
{- Monoid instance for lists -}
let monoid () = {append=append, empty=[]}
test "monoid.concat flattens nested lists" =
assert.equal [:scrap, :your, :typeclasses]
(monoid.concat (monoid ()) [[:scrap, :your], [], [:typeclasses]])
test "a monoid instance can be used as a semigroup instance" =
assert.equal [:scrap, :your, :typeclasses]
(semigroup.concat (monoid ()) [:scrap] [[:your], [], [:typeclasses]])
module int
{- You can define multiple instances without newtype wrappers.
Full disclosure: rebar dies rather than running this file for me.
-}
let addition () = {empty=0, append=fn x y -> x + y}
let multiplication () = {empty=1, append=fn a b -> a * b}
test "addition works" =
assert.equal 6 (monoid.concat (addition ()) [1,2,3])
test "multiplication works" =
assert.equal 9 (monoid.concat (multiplication ()) [1, 3, 3])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment