Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'd like to try pushing the Haskell-is-an-OO-language bit a little further. I think Haskell98 is lacking, but with existential types you can model process entities in a way that I think is often underappreciated. As an off-the-cuff example, consider a counter. A simple approximation might be to say that (Int, Int -> Int) models a counter because it models the state of one, but it also fixes the implementation and fails to encapsulate.

But we can do better with existential types

    data Counter = 
      forall x . Counter { _step  :: x -> x
                         , _view  :: x -> Int
                         , _state :: x
                         }

    step :: Counter -> Counter
    step c@(Counter {..}) = c { state = _step _state }

    view :: Counter -> Int
    view (Counter {..}) = _view _state
At this point, we're passing around the internal state as "some type x" which can never be inspected, achieving encapsulation. We can create general functions that manipulate Counters

    stepTwiceThenShowEm :: Counter -> Int
    stepTwicethenShowEm = view . step . step
and we can easily create alternative implementations

    intCounter, integerCounter, listCounter, unCounter :: Counter

    intCounter     = Counter succ  id        (0 :: Int)
    integerCounter = Counter succ  id        (0 :: Integer)
    listCounter    = Counter (():) length    []
    unCounter      = Counter id    (const 4) ()
We also now must start thinking of our "objects" as entities because we cannot compare their internal state for equality (unless we give it an Eq constraint). This begins to quickly demand we keep some notion of state as we need to model "pointer equality" pretty quickly.

We can even do HAS A subtyping. Lenses make this very powerful.

    data TwoCounters = 
      forall x . TwoCounters { _c1    :: Lens' x Counter
                             , _c2    :: Lens' x Counter
                             , _state :: x
                             }
There's no doubt that this is an uncommon technique in Haskell, but it's worth stating that there are a LOT of similarities between codata modeling like this and typeclasses.

Finally, I began to follow a lot of this due to a middling popular library `folds` [1] which describes very generic ways of traversing data structures and accumulating some kind of result state. Basically all of the types dealt with in this library (L1, L1', M1, R1, L, L', M, R) are existentially quantified codata "objects" like I described above. This makes for very general code.

[1] http://hackage.haskell.org/package/folds



By the way, for those who aren't familiar with Lenses, it's not a bad thing to think of them as getter/setter pairs (which should be a little unsurprising at this point). They're first class, more principled, and more generalizable than your typical getter/setter pair... but my use of them here is far from unintentional.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: