多态函数可以在两种类型之间进行转换

时间:2014-05-12 18:26:36

标签: haskell polymorphism

是否可以在Haskell98中编码或使用以下函数f扩展?

f :: (a -> b) -> (b -> a) -> c -> d
where (c,d) :: (a,b) || (c,d) :: (b,a)

我正在尝试创建一些泛型函数来在ab之间进行转换,但是两者都有些困难。然而,它们会被第一个参数a ->b立即设置,所以我希望这是可能的。任何指导都表示赞赏......

谢谢!

[编辑]

我看到它的方式,我的问题归结为haskell的类型系统是否支持or关键字......因为我的函数的直观签名是:f :: (a->b) -> (b->a) -> a -> b or (a->b) -> (b->a) -> b -> a ..

4 个答案:

答案 0 :(得分:8)

正如alecb解释的那样,它不能以你的签名建议的方式完成,至少通过普通手段(它需要在运行时检查类型)。您可能希望的替代方案是来自lens的{​​{3}}和Iso。如果你的转换函数是完全的和双射的,那么Iso就符合要求。如果其中一个函数是偏的,则可以使用Prism代替。这是一个一次性的例子:

import Control.Lens
import Text.ReadMaybe

stringInt :: Prism' String Int
stringInt = prism' show readMaybe

GHCi> "3" ^? stringInt
Just 3
it :: Maybe Int
GHCi> 3 ^. re stringInt
"3"
it :: String

答案 1 :(得分:6)

假设我们有两个转换函数:

i2s :: Int -> String
s2i :: String -> Int

g = f i2s s2i的类型是什么?我们希望g 1成为合法表达,同时也g "one"。因此g接受IntString。这给我们留下了三个选择:

  1. g是一个完全多态的函数 - 也就是说,它的第一个参数是a类型。因此g Trueg [1,2,3]也是合法的。如此通用的函数与转换无关,你可以证明这种函数不能做任何有趣的事情(例如,a -> Int类型的函数必须是常量的)

  2. g期望某个类型类的参数 - 例如,show是一个仅接受限制类型组的函数(包括Int和{{1} })。但是现在你的函数不是完全多态的 - 它只适用于这种受限类型的组。

  3. 我们可以为此任务定义新的数据类型:

    String

    虽然此功能在某些情况下可能有用,但它不再是通用的(它支持的唯一对是data IntOrString = I Int | S String -- the type signature is slightly different f :: (Int -> String) -> (String -> Int) -> IntOrString -> IntOrString f i2s s2i (I n) = S $ i2s n f i2s s2i (S s) = I $ s2i S Int)。

  4. 所以答案是 - 您可以使用此类型签名定义函数,但它仅适用于特定类型或类型类。

    编辑:不,不支持String类型的函数。这需要动态语言或支持子类型的静态语言。

答案 2 :(得分:6)

我怀疑可以直接做你想做的事,但你可以使用一些解决方法:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

class Converter a b c where
    ($$) :: a -> b -> c

data Conv a b = Conv a b

instance Converter (Conv (a -> b) (b -> a)) a b where
    ($$) (Conv f _) = f

instance Converter (Conv (a -> b) (b -> a)) b a where
    ($$) (Conv _ g) = g

intToString :: Int -> String
intToString = show

stringToInt :: String -> Int
stringToInt = read

printString :: String -> IO ()
printString = print

printInt :: Int -> IO ()
printInt = print

main = do
    let convert = Conv intToString stringToInt
    printString $ convert $$ (12345 :: Int)
    printInt $ convert $$ "12345"

我相信这足够接近你想要的。唯一的区别是强制$$运算符,但这是不可避免的。

更新:您甚至可以删除特殊Conv结构并使用普通元组:

instance Converter (a -> b, b -> a) a b where
    ($$) (f, _) = f

instance Converter (a -> b, b -> a) b a where
    ($$) (_, g) = g

-- ...
printString $ (intToString, stringToInt) $$ (12345 :: Int)
printInt $ (intToString, stringToInt) $$ "12345"

我认为这更接近你的要求。

答案 3 :(得分:3)

我不完全确定你希望如何使用这样的功能,但我的直觉是否定的。假设我们有:

intToString :: Int -> String
intToString = show

stringToInt :: String -> Int
stringToInt = read

foo = f intToString stringToInt

什么是foo的类型?您必须能够将foo同时应用于IntString,这在haskell中通常是不可能的:您可以将foo应用于一个或{另一个但不是两个。

编辑:这不像整个故事 - foo当然可以是foo :: c -> d类型,但这样可以将其应用于任何类型,而不仅仅是IntString。也许在应用于错误类型时可能会抛出错误。即使这样的事情是可能的,但这听起来并不是一个好主意。