, " " " , ?" , : , . , , , .
. . (()) , , . , , Int, a Bool a Char :
type IntBoolChar = ((((), Int), Bool), Char)
, , :
-- we will *not* be using this type like a state monad
addLettuce :: a -> (a, Lettuce)
addLettuce = (, Romaine)
addOlives :: a -> (a, Olive)
addOlives = (, Kalamata)
addCheese :: a -> (a, Cheese)
addCheese = (, Feta)
addGreekSaladIngredients :: a -> (((a, Lettuce), Olive), Cheese)
-- yes, i know you also need tomatoes and onions for a Greek salad. i'm trying to keep the example short
addGreekSaladIngredients = addCheese . addOlives . addLettuce
. . API- #, # currying, Applicative Haskell. : , Add , Build , , , .
, .
data Salad = Salad {
_lettuce :: Lettuce,
_olive :: Olive,
_cheese :: Cheese
}
, , , :
class Has a s where
has :: Lens' s a
-- this kind of function can be written generically using TH or Generics
toSalad :: (Has Lettuce s, Has Olive s, Has Cheese s) => s -> Salad
toSalad x = Salad (x^.has) (x^.has) (x^.has)
( HasX, lens Haskell.)
, , Has . : , , , - . , : ; , .
, . , . , , .
type family Here a as where
Here a (_, a) = True
Here a (_, b) = False
class Has' (here :: Bool) a s where
has' :: Proxy here -> Lens' s a
instance Has' True a (as, a) where
has' _ = _2
instance Has a as => Has' False a (as, b) where
has' _ = _1.has
instance Has' (Here a (as, b)) a (as, b) => Has a (as, b) where
has = has' (Proxy :: Proxy (Here a (as, b)))
. , newtype. , , , Has . :
toSalad :: (((a, Lettuce), Olive), Cheese) -> Salad
toSalad (((_, l), o), c) = Salad l o c
.
:
greekSalad = toSalad $ addGreekSaladIngredients ()
ghci> greekSalad
Salad {_lettuce = Romaine, _olive = Kalamata, _cheese = Feta} -- after deriving Show
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Lens hiding (has, has')
import Data.Proxy
data Lettuce = Romaine deriving (Show)
data Olive = Kalamata deriving (Show)
data Cheese = Feta deriving (Show)
data Salad = Salad {
_lettuce :: Lettuce,
_olive :: Olive,
_cheese :: Cheese
} deriving (Show)
-- we will *not* be using this type like a state monad
addLettuce :: a -> (a, Lettuce) -- <<< Tuple Sections
addLettuce = (, Romaine)
addOlives :: a -> (a, Olive)
addOlives = (, Kalamata)
addCheese :: a -> (a, Cheese)
addCheese = (, Feta)
addGreekSaladIngredients :: a -> (((a, Lettuce), Olive), Cheese)
addGreekSaladIngredients = addCheese . addOlives . addLettuce
class Has a s where
has :: Lens' s a
type family Here a as where
Here a (_, a) = True
Here a (_, b) = False
class Has' (here :: Bool) a s where
has' :: Proxy here -> Lens' s a
instance Has' True a (as, a) where
has' _ = _2
instance Has a as => Has' False a (as, b) where
has' _ = _1.has
instance Has' (Here a (as, b)) a (as, b) => Has a (as, b) where -- <<< Undecidable Instances
has = has' (Proxy :: Proxy (Here a (as, b)))
toSalad :: (Has Lettuce s, Has Olive s, Has Cheese s) => s -> Salad
toSalad x = Salad (x ^. has) (x ^. has) (x ^. has)
greekSalad = toSalad $ addGreekSaladIngredients ()
-- nonSaladsError = toSalad $ (addCheese . addOlives) ()