Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

From C ++ to Haskell Classes and States

I have to transform this C++ code

class A {

public: 
   int x_A;

    void setX_A (int newx) {
        x_A = newx;
    }

    void printX_A() {
       printf("x_A is %d", x_A);
    }
};

class B : public A {
public:
    int x_B;

    void setX_B (int newx) {
       x_B = newx;
    }

    void printX_B() {
       printf("x_B is %d", x_B);
    }

};

main() {
    A objA;
    B objB;
    objA.setX_A(2);
    objA.printX_A();
    objB.printX_A();
    objB.setX_B(5);
    objB.printX_B();
}

into Haskell code, and simulate main() using State (or StateT) Monad.

What I have done so far is this:

import Control.Monad.State
import Control.Monad.Identity

-- Fields For A
data FieldsA = FieldsA {x_A::Int} deriving (Show)

    -- A Class Constructor
constA :: Int -> FieldsA
constA = FieldsA

class A a where
    getX_A :: StateT a IO Int
    setX_A :: Int -> StateT a IO ()
    printX_A :: StateT a IO ()

instance A FieldsA where
    getX_A = get >>= return . x_A
    setX_A newx = do
        fa <- get
        put (fa { x_A = newx })
    printX_A = do
        fa <- get
        liftIO $ print fa
        return ()


data FieldsB = FieldsB{ fa::FieldsA, x_B::Int } deriving (Show)

constB :: Int -> Int -> FieldsB
constB int1 int2 = FieldsB {fa = constA int1, x_B = int2}

class A b => B b where
    getX_B :: StateT b IO Int
    setX_B :: Int -> StateT b IO ()
    printX_B :: StateT b IO ()

-- A Functions for Class B
instance A FieldsB where
    getX_A = do
      (FieldsB (FieldsA x_A) x_B) <- get
      return (x_A)
    setX_A newx = do
        (FieldsB (FieldsA x_A) x_B) <- get
        put (constB newx x_B)
    printX_A = do
        fb <- get
        liftIO $ print fb
        return ()
-- B specific Functions
instance B FieldsB where
    getX_B = get >>= return . x_B
    setX_B newx = do
        fb <- get
        put (fb { x_B = newx })
    printX_B = do
        fb <- get
        liftIO $ print fb
        return ()

test :: StateT FieldsA (StateT FieldsB IO ) ()
test = do
      x <- get
      setX_A 4
      printX_A

      --lift $ setX_A 99
      --lift $ setX_B 99
      --lift $ printX_A
      --lift $ printX_B

      --printX_A
      return ()

go = evalStateT (evalStateT test (constA 1)) (constB 2 3)
--go = runIdentity $ evalStateT (evalStateT test (constA 1)) (constA 1)

test being main().

Now about the problem I have: When I use lift, it works ok, because the function becomes of type StateT FieldsB, but when I try to use setX_A without lift there is a problem

*** Type           : StateT FieldsA IO ()
*** Does not match : StateT FieldsA (StateT FieldsB IO) ()

If I change the type of setX_A to the second one, then It won't work when I use it with lift (because class B is derived from A).

like image 852
epicrose Avatar asked Jun 03 '11 09:06

epicrose


People also ask

Does Haskell have classes?

Haskell classes are roughly similar to a Java interface. Like an interface declaration, a Haskell class declaration defines a protocol for using an object rather than defining an object itself.

Is Ord a subclass of Eq?

Ord is a subclass of Eq that is used for data types that have a total ordering (every value can be compared with another).

What is it called when the type of a function contains one or more class constraints Haskell?

A polymorphic function is called overloaded if its type contains one or more class constraints. (+) :: Num a ⇒ a -> a -> a. For any numeric type a, (+) takes two values of type a and returns a value of type a.

What is instance Haskell?

An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.


1 Answers

First of all, thank you for the amount of detail given, it makes it much easier to understand your problem!

Now, the approach you're taking here is probably not ideal. It introduces a new StateT for each object, which is what's causing a lot of the difficulties you're experiencing, and adding more objects will make things progressively worse. Also complicating matters is that Haskell doesn't have a built-in notion of subtyping, and imitating it with type class contexts will... work, sort of, it's clumsy and not the best.

While I'm sure you realize this is very imperative-style code and translating it to Haskell directly is a bit silly, that's the assignment, so let's talk about ways to do this that are a bit closer to standard Haskell.

Imperative code

State Monad Style:

Setting IO aside for now, to do something like this in pure code the typical approach would be something like:

  • Create a data type holding all your state
  • "Modify" the state with get and put

For output, you can use StateT around IO, or you could add a field to the state data representing the output, holding a list of Strings, and do the whole thing without IO.

This is closest to the "right" way to do your current approach, and is roughly what @Rotsor suggests.

IO Monad Style

The above still requires that all mutable variables be specified beforehand, outside the function, by defining them in the state data. Rather than juggle things this way, you could also imitate the original code more directly and use real, honest-to-god mutable state in IO. Using just A as an example, you'd have something like this:

data FieldsA = FieldsA { x_A :: IORef Int}

constA :: Int -> IO FieldsA
constA x = do xRef <- newIORef x
              return $ FieldsA xRef

class A a where
    getX_A :: a -> IO Int
    setX_A :: a -> Int -> IO ()
    printX_A :: a -> IO ()

instance A FieldsA where
    getX_A = readIORef . x_A
    setX_A = writeIORef . x_A
    printX_A a = getX_A a >>= print

This is conceptually much closer to the original, and is along the lines of what @augustss suggested in the comments on the question.

A slight variation is to keep the object as a simple value, but use an IORef to hold the current version. The difference between the two approaches is roughly equivalent to, in an OOP language, a mutable object with setter methods that change internal state vs. immutable objects with mutable references to them.

Objects

The other half of the difficulty is in modeling inheritance in Haskell. The approach you're using is the most obvious one that many people jump to, but it's somewhat limited. For instance, you can't truly use objects in any context where a supertype is expected; e.g., if a function has a type like (A a) => a -> a -> Bool, there's no simple way to apply it to two different subtypes of A. You'd have to implement your own casting to the supertype.

Here's a sketch of an alternate translation that I would argue is both more natural to use in Haskell, and more accurate to OOP style.

First, observe how all the class methods take the object as a first argument. This represents the implicit "this" or "self" in OOP languages. We can save a step by pre-applying the methods to the object's data, to get a collection of methods already "bound" to that object. We can then store those methods as a data type:

data A = A { _getX_A :: IO Int
           , _setX_A :: Int -> IO ()
           , _printX_A :: IO ()
           }

data B = B { _parent_B :: A 
           , _getX_B :: IO Int
           , _setX_B :: Int -> IO ()
           , _printX_B :: IO ()
           }

Instead of using type classes to provide methods, we'll use them to provide casting to a supertype:

class CastA a where castA :: a -> A
class CastB b where castB :: b -> B

instance CastA A where castA = id
instance CastA B where castA = _parent_B
instance CastB B where castB = id

There are more advanced tricks we could use to avoid making a type class for each pseudo-OOP "class", but I'm keeping things simple here.

Notice that I prefixed the object fields above with underscores. That's because those are specific to the type; now we can define the "real" methods for any type that can be cast to the one we need:

getX_A x = _getX_A $ castA x
setX_A x = _setX_A $ castA x
printX_A x = _printX_A $ castA x

getX_B x = _getX_B $ castB x 
setX_B x = _setX_B $ castB x
printX_B x = _printX_B $ castB x

To construct new objects, we'll use functions that initialize the internal data--equivalent to private members in an OOP language--and create the type representing the object:

newA x = do xRef <- newIORef x
            return $ A { _getX_A = readIORef xRef
                       , _setX_A = writeIORef xRef
                       , _printX_A = readIORef xRef >>= print
                       }

newB xA xB = do xRef <- newIORef xB
                parent <- newA xA
                return $ B { _parent_B = parent
                           , _getX_B = readIORef xRef
                           , _setX_B = writeIORef xRef
                           , _printX_B = readIORef xRef >>= print
                           }

Note that newB calls newA and gets the data type holding its member functions. It can't access the "private" members of A directly, but it could replace any of A's functions if it wanted to.

Now we can use these in a way that's almost identical, both in style and meaning, to your original, e.g.:

test :: IO ()
test = do a <- newA 1
          b <- newB 2 3
          printX_A a
          printX_A b
          setX_A a 4
          printX_A a
          printX_B b
like image 66
C. A. McCann Avatar answered Sep 27 '22 19:09

C. A. McCann