我不能得到我的haskell代码模块化,因为我想要它是。我可能只是被困在我的面向对象范例中,很难从功能上思考,但我完全被难住了。
我有一个数据和两个操作它的函数:
data TruthType = TT_Boolean String
| TT_Percent Double
conjunction :: TruthType -> TruthType -> TruthType
disjunction :: TruthType -> TruthType -> TruthType
通常情况下,您应该将这些函数相邻地实现,像这样:
conjunction :: TruthType -> TruthType -> TruthType
conjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x*y)
conjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t"
conjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "f"
conjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "f"
conjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f"
disjunction :: TruthType -> TruthType -> TruthType
disjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x + (1-x)*y)
disjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t"
disjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "t"
disjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "t"
disjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f"
编译和运行完全像我期望的那样。问题是,我计划实现大约20个不同的truthtype,并且每个truthtype都有更多的函数。因此,根据它们所作用的TruthType构造函数对函数进行分组更有意义:
-- TT_Percent
conjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x*y)
disjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x + (1-x)*y)
-- TT_Boolean
conjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t"
conjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "f"
conjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "f"
conjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f"
disjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t"
disjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "t"
disjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "t"
disjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f"
如果这两个部分都在同一个文件中,我得到一个编译错误,声称我正在重新定义连接和析取函数。我不想抹掉旧的定义,我希望两个定义都有效。我可以使用任何编译器标志来允许这种重新定义吗?
最终,我的目标是让每一个不同的truthtype定义在它自己的文件中。如果我这样做,就会得到歧义错误,因为它不知道该使用哪个函数。是否有一种方法可以让GHC尝试所有这些方法,因为实际上只有一个将在被调用的TruthType上定义?
p。这似乎是类型类的一个很好的用例,但实际上并非如此。我必须能够编写返回TruthType的"实例"的函数,类似于本例中的"classReturn"函数:
class (Show a, Eq a) => TruthClass a where
conjunction :: a -> a -> a
disjunction :: a -> a -> a
instance TruthClass Bool where
conjunction True True = True
conjunction True False = False
conjunction False True = False
conjunction False False = False
disjunction True True = True
disjunction True False = True
disjunction False True = True
disjunction False False = False
instance TruthClass Double where
conjunction x y = x*y
disjunction x y = x + (1-x)*y
classReturn :: (TruthClass a) => String -> a -- This fails to compile because it would allow the failure function below, which violates conjunction's type
classReturn "True" = True
classReturn "False" = False
classReturn "1" = 1
classReturn "0" = 0
failure = conjunction (classReturn "True") (classReturn "1")
编辑:
好了,我现在可以更好地解释为什么我不能让类型类工作,以及为什么提供的解决方案对我不起作用。请看下面的内容(基于augustss的解决方案):
*Main> conjunction True True -- works because type is inferred
True
*Main> classReturn "True" :: Bool -- works because type is explicitly stated
True
*Main> classReturn "True" -- does not work, but this is what I need
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`TruthClass a'
arising from a use of `classReturn' at <interactive>:1:0-17
Probable fix: add a type signature that fixes these type variable(s)
在我的程序中,我将无法指定它是哪种类型。我正在使用parsec解析输入文件。当它碰到一行"#bool"时,所有随后创建的变量都应该是TT_Boolean类型的。当它到达"#percent"时,所有后续变量的类型都应该是TT_Percent。因此,当我调用一个函数时,我不能硬编码该类型是什么,如果使用类型类,似乎必须硬编码。使用数据的解决方案解决了这个问题,但是遇到了数据导致的缺乏模块化的问题。
class (Read a, Show a, Eq a) => TruthClass a where
conjunction :: a -> a -> a
disjunction :: a -> a -> a
classReturn :: String -> a
classReturn = read
instance TruthClass Bool where
conjunction = (&&)
disjunction = (||)
instance TruthClass Double where
conjunction x y = x*y
disjunction x y = x + (1-x)*y
但你也可以保留你的原始设计,只是你不能在conjunction
的方程之间有disjunction
的方程,反之亦然。
一个函数由它所有的方程组成,但是它们必须在源代码中连续出现。
编辑:展示一个Mike想要做的事情的例子:
如果你有那么多子句,你可以把一个伟大的函数拆分成多个:
conjunction PrincipleCase1 = conjunctionForCase1 ...
conjunction PrincipleCase2 = conjunctionForCase2 ...
,然后你可以把处理详细案例的函数放在不同的位置、模块等。