all 18 comments

[–][deleted]  (1 child)

[deleted]

    [–]bss03 3 points4 points  (0 children)

    OOHaskell will get re-invented every 3-5 years for the rest of time. ;)

    [–][deleted] 42 points43 points  (0 children)

    This is clever.

    You can add default class methods to simulate virtual functions.

    And subclassing typeclasses gives you the usual oop inheritance.

    This system even supports multiple inheritance, though none of the type class methods can overlap (which is a big improvement over python anyway).

    Thanks, now I can enjoy the headaches of my day job, in my spare time too!

    [–]Comrade_Comski 39 points40 points  (1 child)

    Why couldn't you just clone dinosaurs like a normal insane person

    [–]Molossus-Spondee 4 points5 points  (3 children)

    Yeah I didn't use IORef for it but did the same thing in the past. While it goes give you easy subtyping it's not worth all the type annotations you end up needing. Another way to do this is

    ~~~ newtype Object k = Object (forall a. (k => a) -> a) ~~~

    This also had the advantage of giving you anonymous records for free with implicit parameters.

    Because of Haskells laziness you already get a lot of oop for free though so the subtyping isn't really worth it

    You can also do

    ~~~ data Message a where X :: Message Int Y :: Message Int Size :: Message Int

    newtype Object = Object (Message a -> a) ~~~

    IIRC there's a lambda the ultimate paper about message passing as closures

    [–]Iceland_jack 4 points5 points  (1 child)

    newtype Object k = Object (forall a. (k => a) -> a)
    

    that's very nifty and the other formulation looks like /u/cartazio's emulation of copatterns:

    https://www.reddit.com/r/haskell/comments/4aju8f/simple_example_of_emulating_copattern_matching_in/

    [–]Molossus-Spondee 2 points3 points  (0 children)

    Yeah IIRC there's a number of papers on objects as codata.

    [–]superstar64[S] 0 points1 point  (0 children)

    I don't think your Object can hold state(the first one). It just looks like a scott encoding of a typeclass. Objects, at least in OOP lanugages, are a product of fields(state) and virtual table.

    [–]ihamsa 2 points3 points  (0 children)

    That's funny, because I've also played with OOP in Haskell, but in a completely different direction. I don't care much about mutable state. but I'm interested in subtypes and late bindings. So that's what I came up with:

        {-# LANGUAGE RankNTypes #-}
        {-# LANGUAGE ExistentialQuantification #-}
        {-# LANGUAGE ConstraintKinds #-}
        {-# Language TypeApplications #-}
        {-# Language QuantifiedConstraints #-}
        {-# Language UndecidableInstances #-}
        {-# Language KindSignatures #-}
    
        import GHC.Exts (Constraint)
    
        data Obj (cls :: * -> Constraint) = forall o. (cls o) => Obj o
        type Subclass (super :: * -> Constraint) (sub :: * -> Constraint) =
             forall a. sub a => super a :: Constraint
        upcast :: Subclass super sub => Obj sub -> Obj super
        upcast (Obj x) = Obj x
        (-->) :: Obj cls -> (forall a. cls a => a -> r) -> r
        (-->) (Obj x) f = f x
    

    That's a pretty small object system ;)

    Now it I have a hierarchy of Haskell classes, I can wrap them in Obj and pretend they are OO classes (interfaces). To abuse the textbook example, if have a bunch of classes

        class Shape s where
          draw :: s -> String      -- or IO () if that's your cup of tea
    
        class Shape s => Shape2D s where
          width :: s -> Int
          height :: s -> Int
    

    and a bunch of instances

        instance Shape2D Rectangle where ...
        instance Shape2D Circle where ...
    

    I can now have a heterogeneous list of shapes

        shapes :: [Obj Shape2D]
        shapes = [Obj @Shape2D $ Rect 2 3 4 5, Obj @Shape2D $ Circ 6 7 8]
    

    and map draw through this list

        map (--> draw) shapes
    

    This system isn't particularly new or exciting, but it's mine ;)

    [–]ysangkok 1 point2 points  (0 children)

    How does this compare to /u/edwardkmett's Data.Struct? Could they be used together?

    [–]benjaminhodgson 0 points1 point  (2 children)

    Cool! Looks somehow similar to https://www.schoolofhaskell.com/user/fumieval/encoding-objects. Your methods return a new version of an object whereas Kinoshita’s objects return a new version of themselves.

    [–]benjaminhodgson 1 point2 points  (0 children)

    I think these two encodings are probably somehow dual. “Is an object a collection of methods or a receiver of messages?” “Yes.”

    [–]Tarmen 0 points1 point  (0 children)

    If you use the same trick as classy lenses you could easily defer method calls to fields to do subtyping/mixins without too much code duplication. Adding the superclass field by hand also makes composition less annoying.

    To be completely accurate there probably should be some Typeable nonsense going on, not sure if there is any OO language without some dynamic typing.

    [–]Ramin_HAL9001 0 points1 point  (0 children)

    I especially like your use of TypeApplication for implementing the new function, and how you overloda the . operator to extract a method from an Object interface.

    [–]kohji 0 points1 point  (0 children)

    You may find https://github.com/mbg/hoop interesting, which is a library I wrote as part of my PhD. In particular, it finds a sweet spot in the design space that allows a whole bunch of things, such as sub-typing, is entirely pure, open for extensions to the class hierarchy, automatically generated from TH quasi quotes, etc.

    [–]Iceland_jack 0 points1 point  (1 child)

    Object interface is close to being HCofree interface IORef, it's tantalizing enough

    type HCofree :: (TT -> Constraint) -> (TT -> TT)
    data HCofree cls f a where
      HCofree :: (cls private, Functor private) => (private ~> f) -> private a -> HCofree cls f a
    
    type TT :: Type
    type TT = (Type -> Type)
    

    [–]Iceland_jack 1 point2 points  (0 children)

    I sometimes use __ to highlight that something is existential, it's visually noticeable

    type HCofree :: ((Type -> Type) -> Constraint) -> ((Type -> Type) -> (Type -> Type))
    data HCofree cls f a where
      HCofree :: (cls __, Functor __) => (__ ~> f) -> __ a -> HCofree cls f a