是否可以在Haskell98中编码或使用以下函数f
扩展?
f :: (a -> b) -> (b -> a) -> c -> d
where (c,d) :: (a,b) || (c,d) :: (b,a)
我正在尝试创建一些泛型函数来在a
和b
之间进行转换,但是两者都有些困难。然而,它们会被第一个参数a ->b
立即设置,所以我希望这是可能的。任何指导都表示赞赏......
谢谢!
[编辑]
我看到它的方式,我的问题归结为haskell的类型系统是否支持or
关键字......因为我的函数的直观签名是:f :: (a->b) -> (b->a) -> a -> b or (a->b) -> (b->a) -> b -> a
..
答案 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
接受Int
和String
。这给我们留下了三个选择:
g
是一个完全多态的函数 - 也就是说,它的第一个参数是a
类型。因此g True
和g [1,2,3]
也是合法的。如此通用的函数与转换无关,你可以证明这种函数不能做任何有趣的事情(例如,a -> Int
类型的函数必须是常量的)
g
期望某个类型类的参数 - 例如,show
是一个仅接受限制类型组的函数(包括Int
和{{1} })。但是现在你的函数不是完全多态的 - 它只适用于这种受限类型的组。
我们可以为此任务定义新的数据类型:
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
)。
所以答案是 - 您可以使用此类型签名定义函数,但它仅适用于特定类型或类型类。
编辑:不,不支持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
同时应用于Int
和String
,这在haskell中通常是不可能的:您可以将foo
应用于一个或{另一个但不是两个。
编辑:这不像整个故事 - foo
当然可以是foo :: c -> d
类型,但这样可以将其应用于任何类型,而不仅仅是Int
或String
。也许在应用于错误类型时可能会抛出错误。即使这样的事情是可能的,但这听起来并不是一个好主意。