Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The answer is very much "practicality issue". Haskell's more advanced type level features (including GADTs and type families) are very much suited for this, but they're also the sort of thing that gives Haskell a reputation for being complicated. If your just using Haskell's core features the way the parent post does, Haskell is a very simple, very elegant language.

But better yet, it certainly does have the big guns which you can pull out.

    -- Just like before, we define `Class` and `Weapon`:
    data Class = Warrior | Wizard
    data Weapon = Sword | Staff | Dagger

    -- The one really annoying thing is that
    -- at the moment you have to use a little bit
    -- of annoying boilerplate to define singletons
    -- (not related to the OOP concept of singletons, by
    -- the way), or use the `singletons` library. In the
    -- future, with DependentHaskell, this won't be necessary:
    data SWeapon (w :: Weapon) where
      SSword :: SWeapon 'Sword
      SStaff :: SWeapon 'Staff
      SDagger :: SWeapon 'Dagger

    -- Now we can define `Player`:
    data Player (c :: Class) where
      WizardPlayer :: AllowedToWield 'Wizard w ~ 'True => SWeapon w -> Player 'Wizard
      WarriorPlayer :: AllowedToWield 'Warrior w ~ 'True => SWeapon w -> Player 'Warrior
This last part shouldn't be to difficult to understand, if you ignore the SWeapon boilerplate: Player is parameterized over the player's class, with different constructors for warriors and wizards. Each constructor has a parameter for the weapon the player is wielding, which is constrained by the type family (read: type-level function) named AllowedToWield.

AllowedToWield isn't that complicated either, it's just a (type-level) function that takes a Class and a Weapon and returns a `Bool` using pattern matching:

    type family AllowedToWield (c :: Class) (w :: Weapon) :: Bool where
      AllowedToWield 'Wizard 'Sword = 'False
      AllowedToWield 'Wizard 'Dagger = 'True
      AllowedToWield 'Wizard 'Staff = 'True
      AllowedToWield 'Warrior 'Sword = 'True
      AllowedToWield 'Wizard 'Dagger = 'True
      AllowedToWield 'Wizard 'Staff = 'False
And there it is. What do you gain from all this? Something which it is very had to get in certain other languages: compile-time type checking that there is no code that will allow a wizard to equip a sword, or a warrior to equip a staff.

Once again, I want to make it clear that you absolutely don't need to do this, even in Haskell. You're absolutely allowed to write the simple code like in the parent post. But in my opinion, this is an extremely powerful and useful tool that Haskell lets you take much further than many other languages.

So long story short, the answer to your question is that it is indeed a "practicality issue", although I don't think that my code is that impracticable. It certainly is absolutely not a Haskell limitation: in fact if anything, Haskell makes it a bit too tempting to go in the other direction, and go way overboard with embedding this kind of thing in the type system.



Thanks for the detailed explanation! I'm mostly a dynamic languages programmer, but I've been reading (and enjoying) Type-Driven Development with Idris, and I have a plan to learn Haskell after that. If such a relationship wasn't modellable, I'm not sure I would have bothered after all.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: