2014-05-16 1 views
2

Я пытаюсь создать простую встроенную DSL, которая должна позволять рекурсивным определениям. С этой целью я использую data-reify для преобразования AST выражения в граф.Как сохранить информацию о типе при преобразовании AST в Graph (eDSL)

В следующем примере код Signal представляет собой AST, который может использовать пользователь (см. Сигнал test). Это статически типизировано, поэтому не возможно добавить Signal Double и Signal Int, например.

{-# LANGUAGE GADTs, TypeFamilies #-} 
import Control.Applicative hiding (Const) 
import Data.Reify 

data Value = VFloat64 Double 
      | VFloat32 Float 
    deriving (Show) 

class HasValue a where 
    value :: a -> Value 

instance HasValue Double where 
    value x = VFloat64 x 

instance HasValue Float where 
    value x = VFloat32 x 

data Signal t where 
    Add :: Num t => Signal t -> Signal t -> Signal t 
    Delay :: HasValue t => t -> Signal t -> Signal t 
    Const :: HasValue t => t -> Signal t 

data Node s where 
    NodeAdd :: s -> s -> Node s 
    NodeDelay :: Value -> s -> Node s 
    NodeConst :: Value -> Node s 
    deriving (Show) 

instance MuRef (Signal t) where 
    type DeRef (Signal t) = Node 
    mapDeRef f (Add a b) = NodeAdd <$> f a <*> f b 
    mapDeRef f (Const x) = pure $ NodeConst (value x) 
    mapDeRef f (Delay x a) = NodeDelay (value x) <$> f a 

test :: Signal Double 
test = Add (Const 1.0) test 

Для оценки DSL, АСТ сначала преобразуется в Node типа с использованием reifyGraph из data-reify. В текущей формулировке это включает использование HasValue typeclass для преобразования значений каждого сигнала в тип суммы Value. К сожалению, это делает диаграмму Node эффективно динамически типизированной, потому что для оценки NodeAdd я всегда буду должен проверить, используют ли два аргумента один и тот же конструктор.

Так что мой вопрос: Можно ли сохранить информацию о типе, который был все еще доступен в Signal AST, при преобразовании в Node графа?

Я уже пытался спараметрировать Node с дополнительным типа (т.е. Node s a), , но это не сработало, потому что DeRef (Signal t) должна быть доброй * -> *.

+0

Вы можете включать в себя оценочную часть кода (по крайней мере типов)? Кроме того, вы пробовали 'Node a s' вместо' Node s a', разрешает ли это проблему с 'Deref (Signal t)' kind? – fizruk

+1

Может быть, это помогает? http://hackage.haskell.org/package/data-treify –

+0

Спасибо, этот пакет выглядит весьма интересным. К сожалению, документация немного разрежена ... – bzn

ответ

1

Вместо использования класса типа, почему бы не пометить свое значение типом с использованием GADT, как вы это делали в другом месте?

data Value v where 
    VFloat64 :: Double -> Value Double 
    VFloat32 :: Float -> Value Float 

deriving instance Show (Value v) 

Имея Num t в надстройке конструктор теперь бессмысленно - вы теперь знаете, что t может только когда-либо быть Float или Double.

data Signal t where 
    Add :: Signal t -> Signal t -> Signal t 
    Delay :: Value t -> Signal t -> Signal t 
    Const :: Value t -> Signal t 

Теперь, вместо того, чтобы выбрасывать свои метки, Node держит их:

data Node t s 
    = NodeAdd s s 
    | NodeDelay (Value t) s 
    | NodeConst (Value t) 
    deriving Show 

Теперь нет необходимости в value, так как ваш AST содержит Value тип данных уже:

instance MuRef (Signal t) where 
    type DeRef (Signal t) = Node t 
    mapDeRef f (Add a b) = NodeAdd <$> f a <*> f b 
    mapDeRef _ (Const x) = pure $ NodeConst x 
    mapDeRef f (Delay x a) = NodeDelay x <$> f a 

В вашем примере test был (Fractional t, HasValue t) => Signal t. Теперь, t устанавливается конструктором, а правильный тип может быть выведен:

let test = Add (Const (VFloat64 1.0)) test 

> :t reifyGraph test 
    reifyGraph test :: IO (Graph (DeRef (Signal Double))) 
+0

Жаль, что не отвечаю. В выходные я не мог войти. Ваше решение отвечает на вопрос, поэтому я приму его. – bzn

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

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