我想使用运算符和规则的定义来构建一个规则,如下所示:
type Rule a = [Symbol a]
Run Code Online (Sandbox Code Playgroud)
和符号的定义为:
data Symbol a = Empty | End | LangSym a
Run Code Online (Sandbox Code Playgroud)
现在定义一个<.>可以应用于以下四种情况的运算符:
(<.>)::Symbol a->Symbol a->Rule a
(<.>)::Rule a->Symbol a->Rule a
(<.>)::Symbol a->Rule a->Rule a
(<.>)::Rule a->Rule a->Rule a
Run Code Online (Sandbox Code Playgroud)
显然,我们需要<.>使用 typeclass重载运算符作为
class RuleOperator s1 s2 where
(<.>)::s1 a->s2 a->Rule a
Run Code Online (Sandbox Code Playgroud)
当实例类型类为Symbolas时没有问题
instance RuleOperator Symbol Symbol where
x <.> y = [x, y]
Run Code Online (Sandbox Code Playgroud)
但是当实例有 时Rule a,由于Rule a是类型同义词,我们不能这样做
instance RuleOperator Symbol Rule where
....
Run Code Online (Sandbox Code Playgroud)
和限制是我们无法改变的定义Rule a来
newtype Rule a = R [Symbol a]
Run Code Online (Sandbox Code Playgroud)
由于其他模块依赖于Rule a.
有什么建议可以在不改变Rule?
如果您不想使用 newtype 包装器,则可以为此使用类型系列:
{-# LANGUAGE TypeFamilies, FlexibleInstances, MultiParamTypeClasses #-}
type Rule a = [Symbol a]
data Symbol a = Empty | End | LangSym a
class RuleOperator t1 t2 where
type Elem t1 t2
(<.>) :: t1 -> t2 -> Rule (Elem t1 t2)
instance RuleOperator (Symbol a) (Symbol a) where
type Elem (Symbol a) (Symbol a) = a
s1 <.> s2 = [s1, s2]
instance RuleOperator (Symbol a) (Rule a) where
type Elem (Symbol a) (Rule a) = a
(<.>) = (:)
instance RuleOperator (Rule a) (Symbol a) where
type Elem (Rule a) (Symbol a) = a
r <.> s = r ++ [s]
instance RuleOperator (Rule a) (Rule a) where
type Elem (Rule a) (Rule a) = a
(<.>) = (++)
Run Code Online (Sandbox Code Playgroud)
使类采用完全应用的类型,这样类型同义词就不是问题,然后使用Elem类型系列来恢复a参数。
这里有两种替代方案,它们允许由内向外和由外向内的类型推断。一种使用类型系列和约束:
{-# LANGUAGE TypeFamilies, FlexibleInstances, MultiParamTypeClasses #-}
type Rule a = [Symbol a]
data Symbol a = Empty | End | LangSym a
class (a ~ Elem t1 t2) => RuleOperator t1 t2 a where
type Elem t1 t2
(<.>) :: t1 -> t2 -> Rule (Elem t1 t2)
instance (a ~ b, a ~ c) => RuleOperator (Symbol a) (Symbol b) c where
type Elem (Symbol a) (Symbol b) = a
s1 <.> s2 = [s1, s2]
instance (a ~ b, a ~ c) => RuleOperator (Symbol a) (Rule b) c where
type Elem (Symbol a) (Rule b) = a
(<.>) = (:)
instance (a ~ b, a ~ c) => RuleOperator (Rule a) (Symbol b) c where
type Elem (Rule a) (Symbol b) = a
r <.> s = r ++ [s]
instance (a ~ b, a ~ c) => RuleOperator (Rule a) (Rule b) c where
type Elem (Rule a) (Rule b) = a
(<.>) = (++)
Run Code Online (Sandbox Code Playgroud)
另一个使用函数依赖(有点像类型函数/有约束的族):
{-# LANGUAGE FunctionalDependencies, FlexibleInstances, MultiParamTypeClasses #-}
type Rule a = [Symbol a]
data Symbol a = Empty | End | LangSym a
class RuleOperator t1 t2 a | t1 -> a, t2 -> a where
(<.>) :: t1 -> t2 -> Rule a
instance RuleOperator (Symbol a) (Symbol a) a where
s1 <.> s2 = [s1, s2]
instance RuleOperator (Rule a) (Symbol a) a where
r <.> s = r ++ [s]
instance RuleOperator (Symbol a) (Rule a) a where
(<.>) = (:)
instance RuleOperator (Rule a) (Rule a) a where
(<.>) = (++)
Run Code Online (Sandbox Code Playgroud)
一些推理测试:
sym1, sym2 :: Symbol Int
sym1 = undefined
sym2 = undefined
rule :: Rule Int
rule = undefined
testInsideOut = sym1 <.> rule -- type is inferred
polySym :: Show a => Symbol a
polySym = Empty
polyRule :: Show a => Rule a
polyRule = [Empty]
testOutSideIn :: Rule Int
testOutSideIn = polySym <.> polyRule -- Show instances are resolved to Int
Run Code Online (Sandbox Code Playgroud)