5 ms·
Perhaps the problem is that you're trying to build a class hierarchy in Haskell, when class hierarchies just don't really work in Haskell. What about a compone
by tinco 13y ago
Perhaps the problem is that you're trying to build a class hierarchy in Haskell, when class hierarchies just don't really work in Haskell.
What about a component oriented design? This is the standard architecture for games anyway.
An enemy, actually every entity, would just be a map of components, which can be simple algebraic data types.
The actions can be implemented as a system function with pattern matching or guards, like:
-- The walking system
walk :: Entity -> Entity
walk e = update entity "position" $ walk' (lookup "position" entity)
walk' (x,y) = (x+1,y)
In reality systems usually work on more than one component, so you'd have to make actions that work on tuples of components, but you'll figure it out :)
- lelf 13y ago> class hierarchies just don't really work in Haskell They work beautifully in Haskell class Eq a => Ord a class (Num a, Ord a) => Real a
- seanmcdirmid 13y agoIsn't that more a has-a than an is-a?
- tel 13y agoIt's more that classes just don't behave the same as `class` in an OOP sense. You can create a subtype hierarchy using typeclasses—Functor->Application->Monad->MonadPlus/Applicative is a great example and the `lens` hierarchy is an even more sophisticated one. But these are subtype hierarchies, not subclass hierarchies. Furthermore, classes don't necessarily provide you with the full convenience of an object, though they can come close. Finally, people aren't used to working in codata and Haskell doesn't make it easy, so you have trouble there, too.
- Chattered 13y agoYou can't create subtype hierarchies in Haskell. Haskell doesn't have any subtyping: every value has exactly one type. This is why you cannot use the OOP solution for the game, which requires lists whose elements have a common supertype. Even with existential types (as the author mentions), you still do not have any subtyping. Typeclasses are collections of types. All types in the applicative class are also in the functor class, but none of these types are subtypes of any others.
- seanmcdirmid 13y agoThere's always O'haskell. I believe normal Haskell does support structural subtyping, even if not nominal.
- Sssnake 13y ago>There's always O'haskell. No, there isn't. Last update was in 2001. It was never really alive, but it is very dead now.
- seanmcdirmid 13y agoRight, but it still demonstrates how nominal subtyping could be added to Haskell.
- tel 13y agoExcept there are quantified types, so `forall a. Functor a => a` is the type of "all types instantiating Functor" and `forall a. Applicative a => a` is a subtype. These are commonly used due to the implicit forall quantification. Edit: I'm rusty here, but after re-reading the LSP I'd also say that existential types could exhibit subtyping as the set of statements provable about them is often exactly those provable about the universally quantified type.
- Chattered 13y agoNeither of the types you gave is inhabited, because the 'a' has kind * -> * . I think what you've said about subtyping is muddled. If a type A is a subtype of B, then values of type A should be values of type B. Now let's take an example from the number class hierarchy. Consider 1) forall a. Num a => a 2) forall a. Integral a => a According to what you have written, (2) should be a subtype of (1). That means that if I define n = 1 :: forall a. Integral a => a then I should be able to "upcast" with: n :: forall a. Num a => a However, this doesn't work, since the quantifier now ranges over types that were not in the range of the original quantifier. In the second type, I can instantiate the a to Float, while I cannot do so for the first type. The only formal account of universal quantifiers in type theory, of which I am aware, extends the term language by introducing a capital lambda that ranges over types. So the polymorphic identity function is actually the term Λa. λx:a. x which is a function that takes a type a followed by a value x:a and returns x. This makes good sense of the way number classes work in Haskell. When you write n :: Num a => a n = 1 the actual machine representation of n is going to vary with the type of a. EDIT: Another way of explaining the counterexample: any type in Applicative is also in Functor. But, on one informal interpretation of forall, the intersection of all applicatives is actually a proper superset of the intersection of all functors.