/* Some literate Haskell and Scala to demonstrate 1) phantom types 2) I am a masochist 3) ??? 4) profit! The code is probably best viewed with a syntax colorizer for one language or the other but I've colorized all my comments. > {-# LANGUAGE EmptyDataDecls #-} > module RocketModule(test1, test2, createRocket, addFuel, addO2, launch) where */ object RocketModule { /* None of these data types have constructors, so there are no values with these types. That's okay because I only need the types at compile time. Hence "phantom" - ethereal and unreal. > data NoFuel > data Fueled > data NoO2 > data HasO2 */ sealed trait NoFuel sealed trait Fueled sealed trait NoO2 sealed trait HasO2 /* The Rocket data type takes two type parameters, fuel and o2. But the constructor doesn't touch them. I don't export the constructor so only this module can create rockets. > data Rocket fuel o2 = Rocket */ final case class Rocket[Fuel, O2] private[RocketModule]() /* createRocket produces a rocket with no fuel and no o2 > createRocket :: Rocket NoFuel NoO2 > createRocket = Rocket */ def createRocket = Rocket[NoFuel, NoO2]() /* addFuel takes a rocket with no fuel and returns one with fuel. It doesn't touch the o2 > addFuel :: Rocket NoFuel o2 -> Rocket Fueled o2 > addFuel x = Rocket */ def addFuel[O2](x : Rocket[NoFuel, O2]) = Rocket[Fueled, O2]() /* Similarly, addO2 adds o2 without touching the fuel > addO2 :: Rocket fuel NoO2 -> Rocket fuel HasO2 > addO2 x = Rocket */ def addO2[Fuel](x : Rocket[Fuel, NoO2]) = Rocket[Fuel, HasO2]() /* launch will only launch a rocket with both fuel and o2 > launch :: Rocket Fueled HasO2 -> String > launch x = "blastoff" */ def launch(x : Rocket[Fueled, HasO2]) = "blastoff" /* This is just a pretty way of stringing things together, stolen shamelessly from F#. Adding infix operations is a bit verbose in Scala. > a |> b = b a */ implicit def toPiped[V] (value:V) = new { def |>[R] (f : V => R) = f(value) } /* Create a rocket, fuel it, add o2, then launch it > test1 = createRocket |> addFuel |> addO2 |> launch */ def test1 = createRocket |> addFuel |> addO2 |> launch /* This compiles just fine, too. It doesn't matter which order we put in the fuel and o2 > test2 = createRocket |> addO2 |> addFuel |> launch */ def test2 = createRocket |> addO2 |> addFuel |> launch //This won't compile - there's no fuel // > test3 = createRocket |> addO2 |> launch // def test3 = createRocket |> addO2 |> launch // This won't compile either - there's no o2 // > test4 = createRocket |> addFuel |> launch // def test4 = createRocket |> addFuel |> launch // This won't compile because you can't add fuel twice // > test5 = createRocket |> addFuel |> addO2 |> addFuel |> launch // def test5 = createRocket |> addFuel |> addO2 |> addFuel |> launch }
Monday, October 18, 2010
Phantom Types In Haskell and Scala
Subscribe to:
Post Comments (Atom)
4 comments:
I wonder what bytecode will be produced by the Scala compiler. How much of that stuff will be optimized away?
Great post! :)
can this be more complicated
I want to do this but suppose you had lots of other parameters that you want to check at various specific points. Unfortunately you are then carrying around a lot of phantom type parameters. Would it be possible in Haskell to combine them in a single blob somehow?
This seems to answer the question for Scala, but I'm not using that!
https://gist.github.com/2880013
Post a Comment