Я использую бесплатную монаду для создания DSL. Как часть языка, существует команда input
, цель состоит в том, чтобы отразить, какие типы ожидаются входным примитивом на уровне типа для дополнительной безопасности.
Например, я хочу написать следующую программу.
concat :: Action '[String, String] ()
concat = do
(x :: String) <- input
(y :: String) <- input
output $ x ++ " " ++ y
Наряду с оценочной функцией
eval :: Action params res -> HList params -> [String]
eval = ...
Что работает следующим образом.
> eval concat ("a" `HCons` "b" `HCons` HNil)
["a b"]
Вот что я до сих пор.
data HList i where
HNil :: HList '[]
HCons :: h -> HList t -> HList (h ': t)
type family Append (a :: [k]) (b :: [k]) :: [k] where
Append ('[]) l = l
Append (e ': l) l' = e ': (Append l l')
data ActionF next where
Input :: (a -> next) -> ActionF next
Output :: String -> next -> ActionF next
instance Functor ActionF where
fmap f (Input c) = Input (fmap f c)
fmap f (Output s n) = Output s (f n)
data FreeIx f i a where
Return :: a -> FreeIx f '[] a
Free :: f (FreeIx f i a) -> FreeIx f i a
type Action i a = FreeIx ActionF i a
liftF :: Functor f => f a -> FreeIx f i a
liftF = Free . fmap Return
input :: forall a . Action '[a] a
input = liftF (Input id)
output :: String -> Action '[] ()
output s = liftF (Output s ())
bind :: Functor f => FreeIx f t a -> (a -> FreeIx f v b) -> FreeIx f (Append t v) b
bind (Return a) f = f a
bind (Free x) f = Free (fmap (flip bind f) x)
Проблема заключается в том, что liftF
не вводит проверку.
liftF :: Functor f => Proxy i -> f a -> FreeIx f i a
liftF p = Free . fmap Return
Это правильный подход?
Я думал, что вдохновение может прийти из пакета monad. Это и привело к определению Return
и Free
.
Еще одна предыстория: я видел в нескольких местах, что пользователи будут определять DSL таким образом, а затем определить функцию оценки eval :: Action a -> [String] -> a
или что-то подобное. Ясная проблема с этим подходом заключается в том, что все аргументы должны иметь один и тот же тип, и нет статической гарантии того, что будет предоставлено правильное количество аргументов. Это попытка решить эту проблему.