6 ms·
From Object Oriented Programming to Functional Programming
- lelf 13y agoThese exercises have nothing to do with expression problem. The goal of expression problem is ability to add new cases in type and new operations on type without touching existing code (and without compromising static type safety)
- seanmcdirmid 13y agoThey also get the solution wrong: only OOP can define new variants and only FP can define new operations. Actually, there are plenty of language, FP and OOP, where it is easy to define new variants and new operations orthogonally without sacrificing separate compilation (e.g. any OO language with mixins like Scala). Mads published a paper [1] on some solutions back in 2004. [1] http://lambda-the-ultimate.org/node/2232 http://lambda-the-ultimate.org/node/2232
- tel 13y agoIt's also easy enough to define new variants in Haskell if you package things as codata. I feel it's less to do with OOP/FP and more to do with whether you're using data or codata.
- icarus127 13y agoCould you elaborate or provide links to examples of what you mean by solving this with codata?
- tel 13y agoMy terminology isn't great, but what I'm talking about in practice is usually what happens as the next step after someone gets talked down from using existential types all over the place. I'm personally a big fan of existential types, but they usually don't need to be explicit. One place that shows up on Hackage that's worth studying is Edward Kmett's `folds` library. The type, Data.Fold.L.L is a codata type data L = forall r . L (r -> b) (r -> a -> r) r It uses existential typing for efficiency, but doesn't need to. Instead, it just helps to show the encapsulation going on here---you literally cannot examine `r` in any way besides applying some of the bundled functions (methods, destructors). The existential type highlights that L is acting as codata, but it's usually not recommended in practice as existential types are a little hard to manage. You could write L more simply and eliminate the `r` data data L = L b (a -> L) by just pre-applying the existentially typed data to each of its destructors and rebundling new L types where necessary. In any case, the point is that it's easy to build a variety of L variants, but hard to extend new operations on Ls.
- tel 13y agoBtw, here are a few example variants of `L` sum :: Num a => L a a sum = L id (+) 0 prod :: Num a => L a a prod = L id (*) 1 list :: L a [a] list = id (\r a -> a : r) []
- jokoon 13y agoAny book that sounds like "functional programming in practice" for C++ programmers ?
- tinco 13y agoPerhaps 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.
- flohofwoe 13y agoThe proposed class hierarchy for different types of "game objects" is exactly the type of hierarchy which will bite you in the ass very soon when adding more features, an entity-component-system would be a better solution for this specific problem (composition instead of inheritance). Newbie programmers should never be taught this "animal - cat - dog" nonsense IMHO because this approach of modelling software after real-world objects doesn't work for anything slightly more complex. For instance, what if you have two different enemy types which both need to implement a common feature but differ in others? Please don't say 'multiple inheritance' ;) This doesn't mean that OOP is bad at all IMHO, just that it shouldn't be treated as a holy grail (applies to all design principles), and doesn't mean that you have to go all-functional. All IMHO of course.