返回两种类型的相同类型类中的一种

Jim*_*ies 5 haskell types typeclass

我有以下类型类

class MyClass c where
  aFunction :: c -> Bool
Run Code Online (Sandbox Code Playgroud)

和两个不同数据类型的两个实例

data MyDataType1 = MyDataType1

instance MyClass MyDataType1 where
  aFunction c = True

data MyDataType2 = MyDataType2

instance MyClass MyDataType2 where
  aFunction c = False
Run Code Online (Sandbox Code Playgroud)

我想写一个函数一个函数,它接受类型类MyClass的两个参数(可能是相同的数据类型或者可能是不同的并返回其中一个.我正在努力找出这个的类型签名,我想我可能采取错误的方法.

这是正确的吗?如果不是我应该使用什么呢?

chooseOne :: (MyClass a, MyClass b) => a -> b -> ?
chooseOne x y = if (aFunction x) then x else y
Run Code Online (Sandbox Code Playgroud)

And*_*ewC 11

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

chooseOne :: (MyClass a, MyClass a) => a -> a -> a
Run Code Online (Sandbox Code Playgroud)

这不是你的意思.

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

data Either a b = Left a | Right b
Run Code Online (Sandbox Code Playgroud)

所以你会的

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

但我宁愿写那个

chooseOne :: (MyClass a, MyClass b) => a -> b -> Either a b
chooseOne x y | aFunction x = Right x 
              | otherwise   = Left y
Run Code Online (Sandbox Code Playgroud)


J. *_*son 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
Run Code Online (Sandbox Code Playgroud)

最后,即使是在动态语言,你必须有一些代码,同时处理了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
Run Code Online (Sandbox Code Playgroud)

为了您的具体情况,因为这两个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'
Run Code Online (Sandbox Code Playgroud)

但这实际上不会打字!如果仔细查看可能找到问题的类型 - 我们传入的处理函数是专用的a,因此不能应用于类型的Right一侧.因此我们需要使用,一个启用的扩展.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
Run Code Online (Sandbox Code Playgroud)

这可以确保f您传入的任何函数eitherOfMyClass对于任何实例都是通用的MyClass,因此可以应用于您ab您的任何实例Either.