Я использую библиотеку Bound для представления лямбда-терминов:
data Exp a = Var a
| Exp a :@: Exp a
| Lam (Scope () Exp a)
Чтобы использовать abstract
и instantiate
с помощью Exp
, У меня установлен экземпляр монады:
instance Monad Exp where
return = Var
Var a >>= f = f a
(x :@: y) >>= f = f >>= x :@: f >>= y
Lam e >>= f = Lam $ e >>>= f
(Где >>>=
определяется в Bound.)
Теперь я хотел бы сделать аннотированную версию типа выше. Я думал, что просто сделаю
data Exp a = Var a
| TypedExp a :@: TypedExp a
| Lam (Scope () TypedExp a)
data TypedExp a = TypedExp Type (Exp a)
Проблема заключается в том, что тип abstract
равен
abstract :: Monad f => (a -> Maybe b) -> f a -> Scope b f a
Это означает, что, если я не хочу просто отказаться от типов при замещении, я должен сделать TypedExp
монаду. Я вижу интуицию для операций: return создает переменную с неограниченным типом, а bind выполняет подстановку с унификацией. Но для создания новых переменных и выполнения объединения мне нужно какое-то состояние.
Поработав некоторое время, я придумал довольно естественные определения для
return' :: a -> MyState (TypedExp a)
bind' :: TypedExp a -> (a -> MyState (TypedExp b)) -> MyState (TypedExp b)
но я не могу сделать шаг к фактическому экземпляру монады, который сделает то, что я хочу.
Могу ли я переплетать типы во что-то, что будет работать с Bound, поскольку оно написано? Должен ли я пойти для написания более общей версии abstract
, что-то вроде...
data Typed f ty a = Typed ty (f ty a)
class TypeLike ty where
data Unification ty :: * -> *
fresh :: Unification ty ty
unify :: ty -> ty -> Unification ty ty
class Annotatable f where
typedReturn :: TypeLike ty => a -> Unification ty (Typed exp ty a)
typedBind :: TypeLike ty => Typed f ty a -> (a -> Unification ty (Typed f ty b)) -> Unification ty (Typed f ty b)
abstract :: (Annotatable f, TypeLike ty) => (a -> Maybe b) -> Typed f ty a -> Unification (Scope b (Typed f ty) a)
... возможно?