2014-09-08 1 views
7

Добрый день, все.Как переосмыслить термин DSL в окончательном безбрежном подходе?

Наше приложение использует типизированную DSL для описания определенной бизнес-логики. DSL поставляется с несколькими переводчиками без тегов.

Вот как его условия были объявлены:

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE EmptyDataDecls #-} 

class Ctl impl where 
    -- Lift constants. 
    cnst :: Show t => t -> impl t 
    -- Obtain the state. 
    state :: impl (Maybe Int) 

    -- Test for equality. 
    eq :: impl Int -> impl Int -> impl Bool 
    -- If-then-else. 
    ite :: impl Bool -> impl t -> impl t -> impl t 

    -- Processing outcomes. 
    retry :: impl Outcome 
    finish :: impl Outcome 

    -- Require a value. 
    req :: impl (Maybe t) -> impl t 

Бизнес-логика затем описывается с помощью фрагментов кода в этом DSL:

proc1 :: Ctl impl => impl Outcome 
proc1 = ite (req state `eq` cnst 5) finish retry 

Эти определения высокого уровня ставятся использовать с переводчиками. У меня есть текстовый интерпретатор, чтобы получить читаемый текстовое описание того, как определяются бизнес-процессы:

newtype TextE t = TextE { evalText :: String } 

instance Ctl TextE where 
    cnst v = TextE $ show v 
    state = TextE "My current state" 
    eq v1 v2 = TextE $ concat [evalText v1, " equals ", evalText v2] 
    ite cond t e = 
    TextE $ 
    concat ["If ", evalText cond, ", then ", evalText t, ", else ", evalText e] 
    retry = TextE "Retry processing" 
    finish = TextE "Finish" 
    req v = TextE $ concat ["(", evalText v, ")*"] 

переводческой на DSL с TEXTE производит строку:

λ> (evalText proc1) :: String 
"If (My current state)* equals 5, then Finish, else Retry processing" 

Такое описание используется в качестве эталона для пользователей/аналитиков.

Я также могу оценить термин DSL для метаязыка (Haskell) с другой переводчик, который, как приложение на самом деле следует правила:

newtype HaskellE t = HaskellE { evalHaskell :: HaskellType t } 

-- Interface between types of DSL and Haskell. 
type family HaskellType t 

instance Ctl HaskellE where 
    cnst v = HaskellE v 
    state = HaskellE dummyState 
    eq v1 v2 = HaskellE $ evalHaskell v1 == evalHaskell v2 
    ite cond t e = 
    HaskellE $ 
    if (evalHaskell cond) 
    then (evalHaskell t) 
    else (evalHaskell e) 
    retry = HaskellE $ print "Retrying..." 
    finish = HaskellE $ print "Done!" 
    req [email protected](HaskellE v) = 
    case v of 
     Just v' -> HaskellE v' 
     Nothing -> 
     HaskellE (error $ 
        "Could not obtain required value from ") -- ++ evalText term) 

-- Dummy implementations so that this post may be evaluated 
dummyState = Just 5 
type Outcome = IO() 
type instance HaskellType t = t 

Этот интерпретатор производит работоспособный код Haskell:

λ> (evalHaskell proc1) :: IO() 
"Done!" 

Теперь к моей проблеме: Я хотел бы использовать интерпретатор TEXTE из HaskellE переводчика. Например, я хочу определить ветвь сбой req способом, который включает текстовое представление вложенного термина (обычно полученное evalText term) в сообщении об ошибке. Соответствующий код закомментирован в версии req для HaskellE выше. Если комментарий вернулся, код выглядит

HaskellE (error $ 
       "Could not obtain required value from " ++ evalText term) 

Однако система типа мешает мне это сделать:

tagless.lhs:90:71: Couldn't match expected type ‘TextE t0’ … 
       with actual type ‘HaskellE (Maybe t)’ 
    Relevant bindings include 
     v :: HaskellType (Maybe t) 
     (bound at /home/dzhus/projects/hs-archive/tagless.lhs:85:22) 
     term :: HaskellE (Maybe t) 
     (bound at /home/dzhus/projects/hs-archive/tagless.lhs:85:7) 
     req :: HaskellE (Maybe t) -> HaskellE t 
     (bound at /home/dzhus/projects/hs-archive/tagless.lhs:85:3) 
    In the first argument of ‘evalText’, namely ‘term’ 
    In the second argument of ‘(++)’, namely ‘evalText term’ 
Compilation failed. 

сообщение в основном говорит, что интерпретатор HaskellE уже был выбран, когда тип переменной impl был создан, а I не может использовать TextE-интерпретатор изнутри HaskellE.

То, что я не могу понять, это: как мне переосмыслить термин от HaskellE до TextE?

Если я совершенно неправильно здесь, как я изменить свой подход, так что я могу фактически использовать текстовый интерпретатор из Haskell один без повторного внедрения его в HaskellE? Похоже, что вполне возможно с первоначальным подходом вместо финального.

Я снял свою фактическую DSL и упростил типы и интерпретаторы ради краткости.

+1

(Что такое " «без привязки» в контексте?) – user2864740

+2

@ user2864740 В этой статье объясняется, что это означает; он обсуждает, какие теги находятся в разделе 3.1: http://okmij.org/ftp/tagless-final/course/lecture.pdf –

+0

@DavidYoung Спасибо! – user2864740

ответ

7

Вы можете отслеживать как значение, так и информацию о выражении, которое создало значение. Если вы это сделаете, вы потеряете некоторые преимущества производительности вашего окончательного представления без тегов.

data Traced t a = Traced {evalTraced :: HaskellType a, trace :: t a} 

Мы рассчитываем использовать его с TextE следа, поэтому мы определим следующие удобства для

evalTextTraced :: Traced TextE a -> HaskellType a 
evalTextTraced = evalTraced 

Этот класс позволяет восстанавливать сообщения об ошибках от trace

class Show1 f where 
    show1 :: f a -> String 

instance Show1 TextE where 
    show1 = evalText 

instance (Show1 t) => Show1 (Traced t) where 
    show1 = show1 . trace 

Этот интерпретатор сохраняет следы любого другого интерпретатора Ctl t, из которого мы можем восстановить сообщения об ошибках при интерпретации Traced t.

instance (Show1 t, Ctl t) => Ctl (Traced t) where 
    cnst v = Traced v (cnst v) 
    state = Traced dummyState state 
    eq (Traced v1 t1) (Traced v2 t2) = Traced (v1 == v2) (eq t1 t2) 
    ite (Traced vc tc) (Traced vt tt) (Traced ve te) = Traced (if vc then vt else ve) (ite tc tt te) 
    retry = Traced (print "Retrying...") retry 
    finish = Traced (print "Done!") finish 
    req (Traced v t) = 
     case v of 
      Just v' -> Traced v' rt 
      Nothing -> Traced (error ("Could not obtain required value from " ++ show1 rt)) rt 
     where rt = req t 

Ваш пример ведет себя, как и ожидалось

print . evalText . trace $ proc1 
evalTextTraced proc1 

"If (My current state)* equals 5, then Finish, else Retry processing" 
"Done!" 

Мы можем еще evalText пример с отказавшим требованием, но при попытке запустить его выдает сообщение об информативной ошибке

proc2 :: Ctl impl => impl Outcome 
proc2 = ite (req (cnst Nothing) `eq` cnst 5) finish retry 

print . evalText . trace $ proc2 
evalTextTraced proc2 

"If (Nothing)* equals 5, then Finish, else Retry processing" 
finaltagless.hs: Could not obtain required value from (Nothing)* 

 Смежные вопросы

  • Нет связанных вопросов^_^