通过将函数定义拆分为多个文件来增加模块化



我不能得到我的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 ...

,然后你可以把处理详细案例的函数放在不同的位置、模块等。

最新更新