Advertisement
Guest User

Untitled

a guest
Sep 30th, 2016
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.16 KB | None | 0 0
  1. -- Our goal is to create a type describing a list of events. This is our
  2. -- type-level DSL.
  3. -- We will then use typeclass resolution to "interpret" this type-level DSL
  4. -- into two things:
  5. -- 1. A comma-separated list of events
  6. -- 2. A method that, when given an event name and a payload, will try to parse
  7. -- that event type with the payload. A form of dynamic dispatching
  8. --
  9. -- To model a list of types we will use tuples. You can imagine the list of
  10. -- types "Int, String, Char" to look like:
  11. --
  12. -- (Int, (String, (Char, EndOfList)))
  13.  
  14. {-# LANGUAGE ScopedTypeVariables #-}
  15. {-# LANGUAGE TypeFamilies #-}
  16.  
  17. module Main where
  18.  
  19. import Data.Proxy (Proxy(Proxy))
  20.  
  21. {------------------------------------------------------------------------------
  22. ---------------- Part 0: Preliminary Definitions ----------------------------
  23. ----------------------------------------------------------------------------}
  24. -- Our events
  25. data Click = Click { clickUser :: String, clickPage :: String } deriving (Show)
  26. data Play = Play { playUser :: String, playTrack :: Int } deriving (Show)
  27. data Pause = Pause { pauseUser :: String, pauseTrack :: Int, pauseTime :: Int } deriving (Show)
  28.  
  29. -- Uninhabitted type for "end of list"
  30. data EndOfList
  31.  
  32. instance Show EndOfList where
  33. show _ = "EndOfList"
  34.  
  35. -- Our list of events
  36. type Events = (Click, (Play, (Pause, EndOfList)))
  37.  
  38. {------------------------------------------------------------------------------
  39. ---------------- Part 1: Extracting Event Names from Types ------------------
  40. ----------------------------------------------------------------------------}
  41.  
  42. -- A typeclass to extract names from a list o events
  43. class EventNames a where
  44. eventNames :: Proxy a -> String -> String
  45.  
  46. -- EventNames: base case for induction on list of types. We start here because
  47. -- *every* list will have EndOfList at the bottom.
  48. instance EventNames EndOfList where
  49. eventNames _ s = s
  50.  
  51. -- A typeclass for types that can be named. Translates Event types to names.
  52. class Named e where
  53. name :: Proxy e -> String
  54.  
  55. -- Instances of Named for our events
  56. instance Named Click where name _ = "click"
  57. instance Named Play where name _ = "play"
  58. instance Named Pause where name _ = "pause"
  59.  
  60. -- EventNames: induction step: (e, tail)
  61. instance (Named e, EventNames tail) => EventNames (e, tail) where
  62. eventNames _ s =
  63. let tailProxy = Proxy :: Proxy tail
  64. eProxy = Proxy :: Proxy e
  65. in eventNames tailProxy (name eProxy ++ ", " ++ s)
  66.  
  67. {------------------------------------------------------------------------------
  68. ---------------- Part 2: Parsing Events / Dynamic Dispatch ------------------
  69. ----------------------------------------------------------------------------}
  70.  
  71. -- A typeclass for types that can be parsed from strings.
  72. class FromString a where
  73. fromString :: String -> Maybe a
  74.  
  75. -- A Helper for working with Strings
  76. maybeRead :: Read a => String -> Maybe a
  77. maybeRead s = case reads s of [(x, "")] -> Just x; _ -> Nothing
  78.  
  79. -- A helper for parsing strings
  80. splitOn :: Eq a => a -> [a] -> [[a]]
  81. splitOn a as =
  82. let recur a' h (interim, acc) =
  83. if a' == h then ([], interim : acc) else (h : interim, acc)
  84. concatTuple (x,y) = x : y
  85. in concatTuple (foldr (recur a) ([], []) as)
  86.  
  87. instance FromString Click where
  88. fromString s =
  89. case splitOn '\t' s of
  90. [user,page] -> Just (Click user page)
  91. _ -> Nothing
  92.  
  93. instance FromString Play where
  94. fromString s =
  95. case splitOn '\t' s of
  96. [user,trackId] -> Play user <$> maybeRead trackId
  97. _ -> Nothing
  98.  
  99. instance FromString Pause where
  100. fromString s =
  101. case splitOn '\t' s of
  102. [user,trackId,ts] -> Pause user <$> maybeRead trackId <*> maybeRead ts
  103. _ -> Nothing
  104.  
  105. class HandleEvent events where
  106. type Out events :: *
  107. handleEvent :: Proxy events -> String -> String -> Either String (Out events)
  108.  
  109. instance HandleEvent EndOfList where
  110. type Out EndOfList = EndOfList
  111. handleEvent _ event payload = Left ("Could not decode " ++ event ++ " with payload " ++ payload)
  112.  
  113. instance (FromString e, Named e, HandleEvent tail) => HandleEvent (e, tail) where
  114. type Out (e, tail) = Either (Out tail) e
  115. handleEvent _ eventName payload =
  116. let handleNext = fmap Left (handleEvent (Proxy :: Proxy tail) eventName payload)
  117. headEventName = name (Proxy :: Proxy e)
  118. in if eventName == headEventName
  119. then case fromString payload of
  120. Nothing -> Left ("Could not decode " ++ payload ++ " for event " ++ eventName)
  121. Just a -> Right (Right a)
  122. else handleNext
  123.  
  124. main :: IO ()
  125. main = do
  126. putStrLn (eventNames (Proxy :: Proxy Events) "")
  127.  
  128. putStr "dynamic dispatch on click: "
  129. print (handleEvent (Proxy :: Proxy Events) "click" "lambdaworld\tpage/rules")
  130.  
  131. putStr "dynamic dispatch on play: "
  132. print (handleEvent (Proxy :: Proxy Events) "play" "lambdaworld\t123")
  133.  
  134. putStr "dynamic dispatch on pause: "
  135. print (handleEvent (Proxy :: Proxy Events) "pause" "lambdaworld\t123\t456")
  136.  
  137. putStr "dynamic dispatch on play with (wrong id): "
  138. print (handleEvent (Proxy :: Proxy Events) "play" "lambdaworld\tnotanumber")
  139.  
  140. putStr "dynamic dispatch on unknown event (\"lambda-world\"): "
  141. print (handleEvent (Proxy :: Proxy Events) "lambda-world" "lambdaworld\t123")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement