我刚开始探索data types à la carte与索引类型相结合的可能性。我目前的实验有点太大而无法包括在这里,但可以找到here。我的例子是将不同成分(算术,函数......)的表达混合在一起。目标是仅强制执行良好类型的表达式。这就是为表达式添加索引(Sort
类型)。
我可以构建如下表达式:
-- define expressions over variables and arithmetic (+, *, numeric constants)
type Lia = IFix (VarF :+: ArithmeticF)
-- expression of integer type/sort
t :: Lia IntegralSort
t = var "c" .+. cnst 1
只要我只构造固定(静态)表达式,这一切都很好。
有没有办法从字符串/其他表示中读取表达式(显然必须对排序进行编码)并生成由这些函子表示的动态值?
例如,我想阅读((c : Int) + (1 : Int))
并以VarF
和ArithmeticF
以某种方式表示它。在这里,我意识到我无法获得静态类型Lia IntegralSort
的值。但是假设我还有:
data EqualityF a where
Equals :: forall s. a s -> a s -> EqualityF a BoolSort
我可以期待有一个功能可以将String
读入Maybe (IFix (EqualityF :+: VarF :+: ...))
。这样的函数将尝试为LHS和RHS构建表示,如果匹配的类型可以产生静态已知类型IFix (EqualityF :+: ...) BoolSort
的结果。问题是LHS(和RHS)的表示没有固定的静态排序。我选择的这种表述是不是我想做的事情?
答案 0 :(得分:1)
(.=.) :: EqualityF :<: f => IFix f s -> IFix f s -> IFix f BoolSort
(.=.) a b = inject (Equals a b)
您可以使用GADT隐藏排序,允许您根据输入返回排序值。然后,模式匹配允许您恢复排序。
data Expr (f :: (Sort -> *) -> (Sort -> *)) where
BoolExpr :: IFix f BoolSort -> Expr f
IntExpr :: IFix f IntegralSort -> Expr f
这是一个简单的后缀表达式解析器,涉及+
和=
。
parse :: (EqualityF :<: f, ArithmeticF :<: f) => String -> [Expr f] -> Maybe (Expr f)
parse (c : s) stack | isDigit c =
parse s (IntExpr (cnst (digitToInt c)) : stack)
parse ('+' : s) (IntExpr e1 : IntExpr e2 : stack) =
parse s (IntExpr (e1 .+. e2) : stack)
parse ('=' : s) (IntExpr e1 : IntExpr e2 : stack) =
parse s (BoolExpr (e1 .=. e2) : stack)
parse ('=' : s) (BoolExpr e1 : BoolExpr e2 : stack) =
parse s (BoolExpr (e1 .=. e2) : stack)
parse [] [e] = Just e
parse _ _ = Nothing
您可能不喜欢=
的重复案例。更通用的框架是Typeable
,允许您只测试所需的类型等式。
data SomeExpr (f :: (Sort -> *) -> Sort -> *) where
SomeExpr :: Typeable s => IFix f s -> SomeExpr f
parseSome :: forall f. (EqualityF :<: f, ArithmeticF :<: f) => String -> [SomeExpr f] -> Maybe (Expr f)
parseSome (c : s) stack | isDigit c =
parseSome s (SomeExpr (cnst (digitToInt c)) : stack)
parseSome ('+' : s) (SomeExpr e1 : SomeExpr e2 : stack) = do
e1 <- gcast e1
e2 <- gcast e2
parseSome s (SomeExpr (e1 .+. e2) : stack)
parseSome ('=' : s) (SomeExpr (e1 :: IFix f s1) : SomeExpr (e2 :: IFix f s2) : stack) = do
Refl <- eqT :: Maybe (s1 :~: s2)
parseSome s (SomeExpr (e1 .=. e2) : stack)
parseSome [] [e] = Just e
parseSome _ _ = Nothing
要解析排序,您希望在类型级别跟踪它们。再次,使用存在类型。
data SomeSort where
SomeSort :: Typeable (s :: Sort) => proxy s -> SomeSort
您可以通过这种方式构建数组:
-- \i e -> array i e
arraySort :: SomeSort -> SomeSort -> SomeSort
arraySort (SomeSort (Proxy :: Proxy i)) (SomeSort (Proxy :: Proxy e)) =
SomeSort (Proxy :: Proxy (ArraySort i e))
这里Typeable
的一个潜在问题是它只允许你测试类型的相等性,当你可能只想检查头构造函数时:你不能问“这个类型是{{1} }?“,但只有”这个类型等于ArraySort
?“或其他一些完整类型。
在这种情况下,您需要一个反映排序结构的GADT。
ArraySort IntSort BoolSort
-- "Singleton type"
data SSort (s :: Sort) where
SIntSort :: SSort IntSort
SBoolSort :: SSort BoolSort
SArraySort :: SSort i -> SSort e -> SSort (ArraySort i e)
data SomeSort where
SomeSort :: SSort s -> SomeSort
array :: SomeSort -> SomeSort -> SomeSort
array (SomeSort i) (SomeSort e) = SomeSort (SArraySort i e)
包提供了各种用于定义和使用这些单例类型的工具,但对于您的用例可能有点过分。