Buona giornata a tutti.Come reinterpretare un termine DSL nell'approccio finale senza tag?
La nostra applicazione utilizza un DSL tipizzato per descrivere determinate logiche di business. La DSL viene fornita con diversi interpreti senza tag.
Ecco come i suoi termini sono dichiarati:
{-# 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
logica di business viene quindi descritto utilizzando blocchi di codice in questo DSL:
proc1 :: Ctl impl => impl Outcome
proc1 = ite (req state `eq` cnst 5) finish retry
Queste definizioni di alto livello sono messi da utilizzare con gli interpreti. Ho un interprete di testo per ottenere una descrizione testuale leggibile di come processi di business sono definiti:
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, ")*"]
Interpretare il DSL con texte produce una stringa:
λ> (evalText proc1) :: String
"If (My current state)* equals 5, then Finish, else Retry processing"
Tale descrizione viene utilizzato come riferimento per utenti/analisti.
posso valutare anche un termine DSL al metalinguaggio (Haskell) con altro interprete, che è come l'applicazione segue in realtà le regole:
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
Questo interprete produce codice Haskell eseguibile:
λ> (evalHaskell proc1) :: IO()
"Done!"
Ora per il mio problema: mi piacerebbe utilizzare l'interprete TextE dall'interprete HaskellE . Ad esempio, desidero definire il ramo guasto di req
in un modo che include la rappresentazione testuale del termine nidificato (normalmente ottenuto da evalText term
) nel messaggio di errore. Il codice rilevante è commentato nell'implementazione req
per HaskellE precedente. Se il commento viene ripristinato, il codice è simile
HaskellE (error $
"Could not obtain required value from " ++ evalText term)
Tuttavia, il sistema di tipo mi impedisce di fare questo:
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.
Il messaggio in pratica dice che l'interprete HaskellE è già stato scelto quando il il tipo variabile impl
è stato istanziato e io non è possibile utilizzare l'interprete TextE dall'interno di HaskellE.
Quello che non riesco a capire è come reinterpretare un termine da HaskellE a TextE?
Se io sono del tutto sbagliato qui, come faccio a rimodellare il mio approccio in modo che possa effettivamente utilizzare l'interprete testo dal Haskell uno senza ri-attuazione all'interno HaskellE? Sembra che sia abbastanza fattibile con approccio iniziale anziché finale.
Ho rimosso la mia DSL effettiva e semplificato i tipi e gli interpreti per motivi di concisione.
(ciò che è " tagless "nel contesto?) – user2864740
@ user2864740 Questo documento spiega cosa significa; discute quali tag sono nella sezione 3.1: http://okmij.org/ftp/tagless-final/course/lecture.pdf –
@DavidYoung Grazie! – user2864740