6

I have the following type class

class MyClass c where
  aFunction :: c -> Bool

and two instances for two different data types

data MyDataType1 = MyDataType1

instance MyClass MyDataType1 where
  aFunction c = True

data MyDataType2 = MyDataType2

instance MyClass MyDataType2 where
  aFunction c = False

I want to write a function a function which takes two parameters of typeclass MyClass (which might be the same data type or might be different and returns one of them. I'm struggling to work out the type signature for this and I think I might be taking the wrong approach.

Would this be correct? If not what should I use instead?

chooseOne :: (MyClass a, MyClass b) => a -> b -> ?
chooseOne x y = if (aFunction x) then x else y
4

4 回答 4

12

你的返回值可以是任何一种类型,所以编译器会抱怨,除非你对两者使用相同的类型,给出

chooseOne :: (MyClass a, MyClass a) => a -> a -> a

这不是你的意思。

要将两种可能不同的类型组合为一种,您可以使用 Either 数据类型:

data Either a b = Left a | Right b

所以你会有

chooseOne :: (MyClass a, MyClass b) => a -> b -> Either a b
chooseOne x y = if (aFunction x) then Right x else Left y

但我宁愿这样写

chooseOne :: (MyClass a, MyClass b) => a -> b -> Either a b
chooseOne x y | aFunction x = Right x 
              | otherwise   = Left y
于 2013-05-11T18:03:23.703 回答
5

您正在编写的函数在 Haskell 中是不可能的——返回类型必须在编译时是固定的和已知的。因此,要编写您感兴趣的内容,您需要一个Either.

chooseOne :: (MyClass a, MyClass b) => a -> b -> Either a b
chooseOne x y = if (aFunction x) then Left x else Right y

最终,即使在动态语言中,您也必须有一些代码可以同时处理ab类型。这“消除”了Either并体现在功能中Data.Either.either

either :: (a -> c) -> (b -> c) -> Either a b -> c
either f _ (Left a)  = f a
either _ g (Right b) = g b

对于您的特定情况,由于ab都是 的实例MyClass,感觉我们可以制作一个更方便的消除函数

eitherOfMyClass :: (MyClass a, MyClass b) => (a -> b) -> Either a a' -> b
eitherOfMyClass f (Left a)   = f a
eitherOfMyClass f (Right a') = f a'

但这实际上不会进行类型检查!如果您仔细查看类型,您可能会发现问题——我们传入的处理函数是专门针对的a,因此不能应用于您的 typeRight一侧。因此,我们需要使用, 启用的扩展。EitherbforallLANGUAGE RankNTypes

{-# LANGUAGE RankNTypes #-}

eitherOfMyClass :: (MyClass a, MyClass b) =>
                   (forall x. MyClass x => (x -> c)) -> Either a b -> c
eitherOfMyClass f (Left a)  = f a
eitherOfMyClass f (Right b) = f b

这确保f您传入的任何函数eitherOfMyClass都真正通用于 的任何实例,MyClass因此可以应用于您a的.bEither

于 2013-05-11T18:05:01.560 回答
1

您总是可以向后执行:您可以接受两个函数作为输入,而不是返回 typex或 type y,并根据您想要“返回”的内容执行一个或另一个:

chooseOne :: (x -> z) -> (y -> z) -> x -> y -> z
chooseOne f1 f2 x y = if aFunction x then f1 x else f2 y

请注意,如果您这样做chooseOne Left Right,您现在拥有Either其他一些人建议的基于 - 的解决方案。您还可以执行类似chooseOne show show返回 aString作为结果的操作。

这种方法是好是坏取决于你为什么要首先构建这个类(即你的程序试图做什么)......

于 2013-05-12T09:09:33.320 回答
1
于 2013-05-12T08:02:40.923 回答