Mi sono ispirato alla recente attività del blog Haskell per provare a scrivere una DSL simile a Forth in Haskell. L'approccio che ho preso è allo stesso tempo semplice e confusa:Polimorfismo di riga in Haskell: problemi di scrittura di Forth DSL con "trasformazioni"
{-# LANGUAGE TypeOperators, RankNTypes, ImpredicativeTypes #-}
-- a :~> b represents a "stack transformation"
-- from stack type "a" to stack type "b"
-- a :> b represents a "stack" where the top element is of type "b"
-- and the "rest" of the stack has type "a"
type s :~> s' = forall r. s -> (s' -> r) -> r
data a :> b = a :> b deriving Show
infixl 4 :>
Per fare le cose semplici, questo funziona abbastanza bene:
start :: (() -> r) -> r
start f = f()
end :: (() :> a) -> a
end (() :> a) = a
stack x f = f x
runF s = s end
_1 = liftS0 1
neg = liftS1 negate
add = liftS2 (+)
-- aka "push"
liftS0 :: a -> (s :~> (s :> a))
liftS0 a s = stack $ s :> a
liftS1 :: (a -> b) -> ((s :> a) :~> (s :> b))
liftS1 f (s :> a) = stack $ s :> f a
liftS2 :: (a -> b -> c) -> ((s :> a :> b) :~> (s :> c))
liftS2 f (s :> a :> b) = stack $ s :> f a b
funzioni semplici possono banalmente essere trasformati in loro trasformazioni pila corrispondenti. Alcuni giocano intorno produce risultati piacevoli finora:
ghci> runF $ start _1 _1 neg add
0
Il guaio arriva quando cerco di estendere questo con funzioni di ordine superiore.
-- this requires ImpredicativeTypes...not really sure what that means
-- also this implementation seems way too simple to be correct
-- though it does typecheck. I arrived at this after pouring over types
-- and finally eta-reducing the (s' -> r) function argument out of the equation
-- call (a :> f) h = f a h
call :: (s :> (s :~> s')) :~> s'
call (a :> f) = f a
call
si suppone trasformare una pila di forma (s :> (s :~> s'))
alla forma s
, da essenzialmente "applicando" la trasformazione (presso la punta della pila) alla "riposo" di esso. Immagino che dovrebbe lavoro come questo:
ghci> runF $ start _1 (liftS0 neg) call
-1
Ma in realtà, mi dà un errore di tipo non corrispondente enorme. Che cosa sto facendo di sbagliato? È possibile che la rappresentazione della "trasformazione dello stack" gestisca sufficientemente le funzioni di ordine superiore o devo modificarla?
N.B. A differenza di come hanno fatto questi ragazzi, invece di start push 1 push 2 add end
, voglio che sia runF $ start (push 1) (push 2) add
, l'idea è che forse più tardi potrò lavorare un po 'di magia di classe per rendere implicito lo push
per determinati valori letterali.
- https://github.com/leonidas/codeblog/blob/master/2012/2012-02-17-concatenative-haskell.md
- https://gist.github.com/1847747
in realtà, mi piacerebbe per sbarazzarsi di 'start' troppo, e solo' runF $ _1 add' _1, anche se io non vedo come sia possibile con questa configurazione . –
I tipi di Impredicato sono una generalizzazione dei tipi di Rank-n, che consentono un forall all'interno di qualsiasi tipo di costruttore, non solo i tipi di funzione. – Carl