我正在写一些代码来表达"表达式"从给定类型的成员构建:
data Expr a = Simply a | Add (Expr a, Expr a) | Mult (Expr a, Expr a)
可以定义一些中缀运算符,以便更方便地构造它们:
(+++) :: Expr a -> Expr a -> Expr a
x +++ y = Add (x, y)
(***) :: Expr a -> Expr a -> Expr a
x *** y = Mult (x, y)
因此可以写
(Simply "bar") +++ ((Simply "fo") *** (Simply "o"))
获取
Add (Simply "bar",Mult (Simply "fo",Simply "o"))
现在的问题是,是否有办法省略构造函数Simply
,即将[{1}}类型的任何成员隐式地视为a
的成员并获取以上示例直接来自Expr a
。
答案 0 :(得分:10)
您可以创建这样的表达式:
class IsExpr a b | a -> b where
ex :: a -> Expr b
这定义了一类类型a,它们是类型b的表达式,其中b被声明为由使用函数依赖项确定。
然后继续将String
变为Expr String
类型的表达式:
instance IsExpr String String where
ex = Simply
此外,有些不足为奇的是,表达式也是表达式:
instance IsExpr (Expr a) a where
ex = id
现在您可以重新定义您的运算符,以便它们可以采用任何产生相同类型表达式的事物的组合,即任何参数可以是String
或Expr String
或您定义的任何{{1} 1}}实例:
IsExpr
这确实需要一些扩展:(++++) :: (IsExpr a c, IsExpr b c) => a -> b -> Expr c
x ++++ y = Add (ex x, ex y)
(****) :: (IsExpr a c, IsExpr b c) => a -> b -> Expr c
x **** y = Mult (ex x, ex y)
(仅当您使用TypeSynonymInstances
s),String
,FlexibleInstances
和MultiParamTypeClasses
时。当然,存在复杂性/收益权衡,因此可能不会带来额外的复杂性。
顺便说一下,我宁愿将数据构造函数定义为FunctionalDependencies
。你正在添加一个
通过使用元组来增加间接性。
答案 1 :(得分:3)
您可以使用Simply
扩展程序删除-XOverloadedStrings
构造函数。
{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}
import Data.String
instance IsString (Expr String) where
fromString = Simply
然后创建一个Num
实例以获取普通运算符
instance Num (Expr a) where
(+) = curry Add
(*) = curry Mult
然后你可以做像
这样的事情"foo" + "bar" + "baz" :: Expr String
所有这一切都是因为GHC将"..."
解释为fromString "..."
。
答案 2 :(得分:2)
如果你坚持在字符串上使用数学运算符,你可以使使用这些操作创建一个新的类型类:Expr a
成为Num
类的实例:
{-# LANGUAGE TypeFamilies #-}
class ExprOps a where
type Internal a
(+++) :: a -> a -> a
(***) :: a -> a -> a
fromInternal :: Internal a -> a
instance ExprOps (Expr a) where
type Internal (Expr a) = a
x +++ y = Add (x,y)
x *** y = Mult (x,y)
fromInternal = Simply
然后你可以简单地写
foo :: Expr String
foo = "bar" +++ "fo" *** "o"
仍然会产生表达式
Add (Simply "bar",Mult (Simply "fo",Simply "o"))
但不需要任何明确的Simply
构造函数。