How to avoid ugly code resolving this problem in Haskell (LANGUAGE extensions)?

I am trying to write a program that mimics several creatures in the world. Basically the word sends a message over the list of creatures, and each creature gives its answer, which, in turn, changes the world.

I simplified what I am trying to write in the following skeleton:

module Structure0 where
type Message = String
class Creature a where
    processInput :: a -> Message -> Message
class World a where
    processAction :: a -> b -> Message -> a
    getCreatures  :: a -> [b]

---- USAGE EXAMPLE ----
data Parrot = Parrot Int deriving Show
instance Creature Parrot where
    processInput p s = s
data ParrotWorld = ParrotWorld [Parrot]
instance World ParrotWorld where
    processAction w p s = w
    getCreatures (ParrotWorld ps) = ps

In this code, I would like the parameter b in the definition of the World class to be able to accept all data values ​​belonging to the Creature class, for example:

processAction :: (Creature b) => a -> b -> Message -> a

Of course, these examples are not actual haskell code, lo let pass illustrate the two solutions found: the first, including ExistentialQuantification:

{-# LANGUAGE ExistentialQuantification #-}
module Structure1 where
type Message = String
class Creature_ a where
    processInput :: a -> Message -> Message
data Creature = forall c. Creature_ c => Creature c
instance Creature_ Creature where
    processInput (Creature c) = processInput c
class World a where
    processAction :: a -> Creature -> Message -> a
    getCreatures  :: a -> [Creature]

---- USAGE EXAMPLE ----
data Parrot = Parrot Int deriving Show
instance Creature_ Parrot where
    processInput u s = s
data ParrotWorld = ParrotWorld [Creature]
instance World ParrotWorld where
    processAction w p s = w
    getCreatures (ParrotWorld ps) = ps

and the second one suggested by the good guy on #haskell using TypeFamilies:

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
module Structure2 where
type Message = String
class Creature a where
    processInput :: a -> Message -> Message
class (Creature (WorldCreature a)) => World a where
    type WorldCreature a :: *
    processAction :: a -> WorldCreature a -> Message -> a
    getCreatures  :: a -> [WorldCreature a]
---- USAGE EXAMPLE ----
data Parrot = Parrot Int deriving Show
instance Creature Parrot where
    processInput p s = s
data ParrotWorld = ParrotWorld [Parrot]
instance World ParrotWorld where
    type WorldCreature ParrotWorld = Parrot
    processAction w p s = w
    getCreatures (ParrotWorld ps) = ps

The main goal of this exercise is to write nice, elegant code. So the questions are:

1) ? ( , - , processInput, , , , .

2) , - Creature Creature_. , []; , , :

\(Creature (Parrot x)) -> x

- . ?

3) : , , Parrot1 Parrot2: ?

4) ? , haskell?

:)

Carlo

+5
1

1

- . , , . , Identity , instance Monad a a, - Monad. , , .

2

Data.Dynamic, , , , . , Haskell, , , Data.Dynamic. , , , .

3

, , . , , , Creature. , numParrots , .

4

. , , " ", , (, ParrotWorld).

, Data.Typeable. , , , .

{-# LANGUAGE DeriveDataTypeable,
             ImpredicativeTypes,
             NoMonomorphismRestriction,
             RankNTypes,
             ScopedTypeVariables #-}

module Test where

import Data.Typeable

type Message = String

class Typeable α => Creature α where
    processInput :: α -> Message -> Message

-- box a creature
type BoxedC = (Message -> Message, Typeable β => Maybe β)
boxC :: Creature α => α -> BoxedC
boxC x = (processInput x, cast x)

class World α where
    -- from your description, I'd not have Creature as part of this.
    processAction :: α -> Message -> α
    getCreatures :: α -> [BoxedC]

data Parrot = Parrot { parrotMessage :: String } deriving Typeable
data Lizard = Lizard { lizardMessage :: String } deriving Typeable

instance Creature Parrot where processInput p _ = (parrotMessage p)
instance Creature Lizard where processInput l _ = (lizardMessage l)

-- NOTE: Keep it simple and use a single World instance
-- (i.e. no typeclass) unless you need it.
data BaseWorld = BaseWorld { creatureList :: [BoxedC] }
instance World BaseWorld where
    processAction w _ = w
    getCreatures = creatureList

w = BaseWorld [boxC $ Parrot "parrot1", boxC $ Lizard "Lizard1"]

numParrots :: [BoxedC] -> Int
numParrots lst = foldl (+) 0 (map (go . snd) lst) where
    go :: (forall β. Typeable β => Maybe β) -> Int
    go (Just x :: Maybe Parrot) = 1
    go _ = 0

test = numParrots (getCreatures w)

: , . , , . , , , , , , , . , . ,

bind_creature :: Creature -> World -> World

, World , ,

data World = World { nextWorld :: World }

, w = World w. ,

transformWorld :: Creature -> World -> World

bind_creature ,

bind_creature c w = World { nextWorld = transformWorld c (nextWorld w) }

, .

+2

All Articles