Tôi đã được lấy cảm hứng từ hoạt động blog Haskell gần đây để thử viết một Forth giống như DSL trong Haskell. Cách tiếp cận tôi đã là đồng thời đơn giản và khó hiểu:Đa hình hàng trong Haskell: rắc rối khi viết Forth DSL với "biến đổi"
{-# 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 :>
Đối với làm những việc đơn giản, hoạt động này khá độc đáo:
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
chức năng đơn giản trivially có thể được biến đổi thành nhiều biến thể ngăn xếp tương ứng của họ. Một số chơi xung quanh mang lại kết quả thú vị cho đến nay:
ghci> runF $ start _1 _1 neg add
0
Vấn đề xảy ra khi tôi cố gắng mở rộng này với các chức năng bậc cao.
-- 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
là nghĩa vụ phải chuyển đổi một chồng các hình thức (s :> (s :~> s'))
mẫu s
, bởi về cơ bản "áp dụng" sự chuyển đổi (tổ chức tại đỉnh của stack) để "nghỉ ngơi" của nó. Tôi tưởng tượng nó nên công việc như thế này:
ghci> runF $ start _1 (liftS0 neg) call
-1
Nhưng trong thực tế, nó mang lại cho tôi một khổng lồ lỗi loại không phù hợp. Tôi đang làm gì sai? Biểu diễn "chuyển đổi ngăn xếp" có thể xử lý đủ các hàm bậc cao hơn hay tôi cần điều chỉnh nó?
N.B. Không giống như cách các chàng trai đã làm điều đó, thay vì start push 1 push 2 add end
, tôi muốn nó là runF $ start (push 1) (push 2) add
, ý tưởng được rằng có lẽ sau này tôi có thể làm việc một số phép thuật typeclass để làm cho các push
tiềm ẩn cho một số literals.
- https://github.com/leonidas/codeblog/blob/master/2012/2012-02-17-concatenative-haskell.md
- https://gist.github.com/1847747
thực sự, tôi cũng muốn loại bỏ 'bắt đầu' và chỉ có' runF $ _1 _1 add', mặc dù tôi không thực sự thấy cách thiết lập này có thể . –
Các kiểu không rõ ràng là sự tổng quát hóa các kiểu Rank-n, cho phép một forall bên trong bất kỳ kiểu hàm tạo nào, không chỉ các kiểu hàm. – Carl