Is there a Haskell equivalent of OOP's abstract classes, using algebraic data types or polymorphism?

后端 未结 3 1678
说谎
说谎 2021-01-30 17:22

In Haskell, is it possible to write a function with a signature that can accept two different (although similar) data types, and operate differently depending on what type is pa

3条回答
  •  情深已故
    2021-01-30 17:43

    Consider this example using TypeClasses.

    We define a c++-like "abstract class" MVC based on three types (note MultiParamTypeClasses): tState tAction tReaction in order to define a key function tState -> tAction -> (tState, tReaction) (when an action is applied to the state, you get a new state and a reaction.

    The typeclass has three "c++ abstract" functions, and some more defined on the "abstract" ones. The "abstract" functions will be defined when and instance MVC is needed.

    {-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, NoMonomorphismRestriction #-}
    
    
    -- -------------------------------------------------------------------------------
    
    class MVC tState tAction tReaction | tState -> tAction tReaction where
          changeState :: tState -> tAction -> tState       -- get a new state given the current state and an action ("abstract")
          whatReaction :: tState -> tReaction              -- get the reaction given a new state ("abstract")
          view :: (tState, tReaction) -> IO ()             -- show a state and reaction pair ("abstract")
    
          -- get a new state and a reaction given an state and an action (defined using previous functions)
          runModel :: tState -> tAction -> (tState, tReaction) 
          runModel s a = let
                                    ns = (changeState s a) 
                                    r = (whatReaction ns) 
                      in (ns, r)
    
          -- get a new state given the current state and an action, calling 'view' in the middle (defined using previous functions)
          run :: tState -> tAction -> IO tState
          run s a = do
                            let (s', r) = runModel s a
                            view (s', r)
                            return s'
    
          -- get a new state given the current state and a function 'getAction' that provides actions from "the user" (defined using previous functions)
          control :: tState -> IO (Maybe tAction) -> IO tState
          control s getAction = do
                  ma <- getAction
                  case ma of
                       Nothing -> return s
                       Just a -> do
                                  ns <- run s a
                                  control ns getAction
    
    
    -- -------------------------------------------------------------------------------
    
    -- concrete instance for MVC, where
    -- tState=Int tAction=Char ('u' 'd') tReaction=Char ('z' 'p' 'n')
    -- Define here the "abstract" functions
    instance MVC Int Char Char where
             changeState i c 
                         | c == 'u' = i+1 -- up: add 1 to state
                         | c == 'd' = i-1 -- down: add -1 to state
                         | otherwise = i -- no change in state
    
             whatReaction i
                          | i == 0 = 'z' -- reaction is zero if state is 0
                          | i < 0 = 'n' -- reaction is negative if state < 0                     
                          | otherwise = 'p' -- reaction is positive if state > 0
    
             view (s, r) = do
                      putStrLn $ "view: state=" ++ (show s) ++ " reaction=" ++ (show r) ++ "\n"
    
    --
    
    -- define here the function "asking the user"
    getAChar :: IO (Maybe Char) -- return (Just a char) or Nothing when 'x' (exit) is typed
    getAChar = do
             putStrLn "?"
             str <- getLine
             putStrLn ""
             let c = str !! 0
             case c of
                  'x' -> return Nothing
                  _ -> return (Just c)
    
    
    -- --------------------------------------------------------------------------------------------
    -- --------------------------------------------------------------------------------------------
    
    -- call 'control' giving the initial state and the "input from the user" function 
    finalState = control 0 getAChar :: IO Int
    
    -- 
    
    main = do
         s <- finalState
         print s
    

提交回复
热议问题