Hacker News new | past | comments | ask | show | jobs | submit login

These exercises have nothing to do with expression problem.

The goal of expression problem is ability to add new cases in type and new operations on type without touching existing code (and without compromising static type safety)




They also get the solution wrong: only OOP can define new variants and only FP can define new operations. Actually, there are plenty of language, FP and OOP, where it is easy to define new variants and new operations orthogonally without sacrificing separate compilation (e.g. any OO language with mixins like Scala). Mads published a paper [1] on some solutions back in 2004.

[1] http://lambda-the-ultimate.org/node/2232


It's also easy enough to define new variants in Haskell if you package things as codata. I feel it's less to do with OOP/FP and more to do with whether you're using data or codata.


Could you elaborate or provide links to examples of what you mean by solving this with codata?


My terminology isn't great, but what I'm talking about in practice is usually what happens as the next step after someone gets talked down from using existential types all over the place.

I'm personally a big fan of existential types, but they usually don't need to be explicit. One place that shows up on Hackage that's worth studying is Edward Kmett's `folds` library. The type, Data.Fold.L.L is a codata type

    data L = forall r . L (r -> b) (r -> a -> r) r	 
It uses existential typing for efficiency, but doesn't need to. Instead, it just helps to show the encapsulation going on here---you literally cannot examine `r` in any way besides applying some of the bundled functions (methods, destructors).

The existential type highlights that L is acting as codata, but it's usually not recommended in practice as existential types are a little hard to manage. You could write L more simply and eliminate the `r` data

    data L = L b (a -> L)
by just pre-applying the existentially typed data to each of its destructors and rebundling new L types where necessary.

In any case, the point is that it's easy to build a variety of L variants, but hard to extend new operations on Ls.


Btw, here are a few example variants of `L`

    sum :: Num a => L a a
    sum = L id (+) 0

    prod :: Num a => L a a
    prod = L id (*) 1

    list :: L a [a]
    list = id (\r a -> a : r) []




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: