Skip to content

Instantly share code, notes, and snippets.

@AndrasKovacs
Last active August 29, 2015 13:56
Show Gist options
  • Save AndrasKovacs/9018361 to your computer and use it in GitHub Desktop.
Save AndrasKovacs/9018361 to your computer and use it in GitHub Desktop.
Gilded Rose kata with lens and ADTs (of course we don't observe the original "don't change the data structure" constraint here).
{-# LANGUAGE
TemplateHaskell, LambdaCase, MultiParamTypeClasses,
FlexibleInstances, FunctionalDependencies #-}
import Control.Applicative
import Control.Lens
import Control.Lens.Extras
data Item = Simple {_sellIn :: Int, _quality :: Int, _itemName :: String}
| Conjured {_sellIn :: Int, _quality :: Int, _itemName :: String}
| Sulfuras
| AgedBrie {_sellIn :: Int, _quality :: Int}
| BackStagePass {_sellIn :: Int, _quality :: Int}
deriving (Eq, Show, Ord)
makeLenses ''Item
makePrisms ''Item
-- overloading "name" as either a Lens or a Traversal
class HasName f s t b | t -> b, s b -> t where
name :: LensLike f s t String b
-- the name of an Item is a Traversal, of course
instance Applicative f => HasName f Item Item String where
name = itemName
clampQual :: Int -> Int
clampQual n = if n < 0 then 0 else if n > 50 then 50 else n
update :: Item -> Item
update = \case
Simple s q name -> Simple (s - 1) (clampQual $ q - (1 + fromEnum (s < 0))) name
Conjured s q name -> Conjured (s - 1) (clampQual $ q - 2*(1 + fromEnum (s < 1))) name
AgedBrie s q -> AgedBrie (s - 1) (clampQual $ q + 1)
Sulfuras -> Sulfuras
BackStagePass s q -> let
increase = if s <= 5 then 3 else if s <= 10 then 2 else 1
q' = if s < 0 then 0 else clampQual $ q + increase
in BackStagePass (s - 1) q'
times :: Int -> (a -> a) -> (a -> a)
times n f = foldr (.) id (replicate n f)
stuff = [
Sulfuras,
Simple 10 30 "foo",
AgedBrie 10 10,
BackStagePass 5 20,
Conjured 30 40 "bar"]
main = do
mapM_ (print . times 5 update) stuff
putStrLn ""
stuff^! each . name . act print
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment