tagless encoding of mutually recursive types
I am trying to express a pair of mutually recursive data types in the final-tagless encoding.
I am able to write:
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE ExplicitForAll #-}
module Test where
class ExprSYM repr where
expr :: forall proc. (ProcSYM proc) => proc Int -> repr
class ProcSYM repr where
varProc :: forall a. String -> repr a
intProc :: String -> repr Int
subjectOf :: forall expr. (ExprSYM expr) => expr -> repr Int
myProc = intProc "proc A."
However, when I write:
myExpr = expr myProc
I receive the following error:
Could not deduce (Test.ProcSYM proc0)
arising from a use of ‘Test.expr’
from the context (Test.ExprSYM repr)
bound by the inferred type of
Test.myExpr :: Test.ExprSYM repr => repr
at src/Test.hs:16:3-22
The type variable ‘proc0’ is ambiguous
In the expression: Test.expr Test.myProc
In an equation for ‘Test.myExpr’:
Test.myExpr = Test.expr Test.myProc
Does any such encoding require the use of functional dependencies (or equivalent) to expess the constraint between the two representation types?
If so, how would I write this?
Let's start by looking at the type of myProc
myProc :: ProcSYM repr => repr Int
myProc = intProc "proc A."
This says, forall types repr where ProcSYM repr , I'm a value of type repr Int . If we had multiple implementations of ProcSYM , this is a value that's polymorphic in all of them. For example, if we had a corresponding tagged GADT ProcSYM' with a ProcSYM instance, myProc could be used as a value of ProcSYM' .
{-# LANGUAGE GADTs #-}
data ProcSYM' a where
VarProc :: String -> ProcSYM' a
IntProc :: String -> ProcSYM' a
instance ProcSYM ProcSYM' where
varProc = VarProc
intProc = IntProc
myProc' :: ProcSYM' Int
myProc' = myProc
The ProcSym repr constraint in myProc :: ProcSYM repr => repr Int is providing a way to construct repr s, which is exactly what myProc does. No matter which ProcSym repr you want, it can construct a repr Int .
The ProcSYM proc constraint in the type of expr :: forall proc. (ProcSYM proc) => proc Int -> repr expr :: forall proc. (ProcSYM proc) => proc Int -> repr is kind of meaningless. The constraint ProcSYM proc is once again providing a means to construct proc s. It can't help us look inside or deconstruct a proc Int . Without a way to look inside proc Int s, we might as well not have the proc Int argument and instead read expr :: repr .
The type forall proc. ProcSYM proc => proc Int forall proc. ProcSYM proc => proc Int (the type of myProc ) on the other hand, promises, no matter how you construct proc s, I can provide a value of that type. You want to pass myProc as the first argument to expr , as evidenced by
myExpr = expr myProc
Passing in a polymorphic value of this type without choosing a concrete proc requires RankNTypes .
class ExprSYM repr where
expr :: (forall proc. ProcSYM proc => proc Int) -> repr
The instance for ExprSYM can choose the ProcSYM dictionary to pass into the first argument. This allows the implementation of expr to deconstruct the proc Int . We'll demonstrate this by completing an example with GADTs to see what this is doing. We will also make the same change to the type of subjectOf .
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GADTs #-}
module Test where
class ExprSYM repr where
expr :: (forall proc. ProcSYM proc => proc Int) -> repr
class ProcSYM repr where
varProc :: forall a. String -> repr a
intProc :: String -> repr Int
subjectOf :: (forall expr. ExprSYM expr => expr) -> repr Int
-- Tagged representation for ExprSYM
data ExprSYM' where
Expr :: ProcSYM' Int -> ExprSYM'
deriving instance Show ExprSYM'
instance ExprSYM ExprSYM' where
expr x = Expr x -- chooses that the ProcSYM proc => proc Int must be ProcSYM' Int
-- Tagged representation for ProcSYM
data ProcSYM' a where
VarProc :: String -> ProcSYM' a
IntProc :: String -> ProcSYM' a
SubjectOf :: ExprSYM' -> ProcSYM' Int
deriving instance Show (ProcSYM' a)
instance ProcSYM ProcSYM' where
varProc = VarProc
intProc = IntProc
subjectOf x = SubjectOf x -- chooses that the ExprSYM repr => repr must be ExprSYM'
-- myProc and myExpr with explicit type signatures
myProc :: ProcSYM repr => repr Int
myProc = intProc "proc A."
myExpr :: ExprSYM repr => repr
myExpr = expr myProc
main = print (myExpr :: ExprSYM')
This outputs an abstract syntax tree for myExpr . We can see that if the implementation of expr wanted to deconstruct the ProcSYM proc => proc Int value it could (and in this case did) provide a ProcSYM dictionary that builds values it knows how to deconstruct. We can see this in the IntProc constructor in the shown value.
Expr (IntProc "proc A.")
链接地址: http://www.djcxy.com/p/87348.html
上一篇: 在MVC 6中调整上传的图像大小
下一篇: 相互递归类型的无标记编码
