Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h3>1 class vs. data</h3> <p>Creature should be a class -- it describes an interface. Data should be used when you think of actually communicating <em>values</em>, or when you need to introduce a new type, wrapping an existing object with new behavior. For example, the <code>Identity</code> monad needs to wrap its values in a new type, or else you'd see <code>instance Monad a</code> for all <code>a</code>, which would cause conflicts with making anything else a <code>Monad</code> instance. But, you may need to wrap it.</p> <h3>2 lists</h3> <p>There is a way to do it with <code>Data.Dynamic</code>, but every time I've thought about doing it that way, I've been able to think of a way to do it with regular typeclasses instead. That said, I haven't written that much Haskell, and many libraries certainly rely on <code>Data.Dynamic</code>. If you want to really unbox a type, then you probably need to use it.</p> <h3>3 extensionality</h3> <p>As before, if you can leave type-specific functionality in the classes, that is best. It'd be most helpful if you could post an example, showing why you can't add another function to <code>Creature</code>. I'll assume you want to count <code>numParrots</code> in the example below, and you really do need to unbox them.</p> <h3>4 general comments</h3> <p>There are always many solutions to a problem. Based on your description, I'd think that "different worlds should entail different types of messages", not that a world should be tied to a specific type of creature (e.g. <code>ParrotWorld</code>).</p> <h2>another solution</h2> <p>here's my solution, using <code>Data.Typeable</code>. As mentioned above, it's my first time using it, so there may be a cleaner way.</p> <pre><code>{-# LANGUAGE DeriveDataTypeable, ImpredicativeTypes, NoMonomorphismRestriction, RankNTypes, ScopedTypeVariables #-} module Test where import Data.Typeable type Message = String class Typeable α =&gt; Creature α where processInput :: α -&gt; Message -&gt; Message -- box a creature type BoxedC = (Message -&gt; Message, Typeable β =&gt; Maybe β) boxC :: Creature α =&gt; α -&gt; BoxedC boxC x = (processInput x, cast x) class World α where -- from your description, I'd not have Creature as part of this. processAction :: α -&gt; Message -&gt; α getCreatures :: α -&gt; [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] -&gt; Int numParrots lst = foldl (+) 0 (map (go . snd) lst) where go :: (forall β. Typeable β =&gt; Maybe β) -&gt; Int go (Just x :: Maybe Parrot) = 1 go _ = 0 test = numParrots (getCreatures w) </code></pre> <p>The idea is similar to yours: we box creatures before we put them in a list. The boxed elements have enough data so that you can unbox the type if you need to. One final thing to mention, though it's maybe not what you want here, is that closures are powerful. You don't need to keep a list of creatures if you can express their results as function composition. For example, in pseudocode, you could have a function</p> <pre><code>bind_creature :: Creature -&gt; World -&gt; World </code></pre> <p>which adds a creature to a world, and World has a type which returns its next iteration,</p> <pre><code>data World = World { nextWorld :: World } </code></pre> <p>which you set to itself for the base, namely <code>w = World w</code>. For simplicity, let's assume that each creature has a function</p> <pre><code>transformWorld :: Creature -&gt; World -&gt; World </code></pre> <p>then you could implement bind_creature like,</p> <pre><code>bind_creature c w = World { nextWorld = transformWorld c (nextWorld w) } </code></pre> <p>hope it helps.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload