[英]Polymorphic function to convert between two types
是否可以在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
...
正如alecb解釋的那樣,它不能以你的簽名建議的方式完成,至少通過普通手段(它需要在運行時檢查類型)。 您可能喜歡的另一種選擇是lens
中的Iso和Prism 。 如果你的轉換函數是完全和雙射的,那么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
假設我們有兩個轉換函數:
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
和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
)。
所以答案是 - 您可以使用此類型簽名定義函數,但它僅適用於特定類型或類型類。
編輯:不,不支持類型a -> b where a is Int or String
的函數, a -> b where a is Int or String
。 這需要動態語言或支持子類型的靜態語言。
我懷疑可以直接做你想做的事,但你可以使用一些解決方法:
{-# 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"
我認為這更接近你的要求。
我不完全確定你希望如何使用這樣的功能,但我的直覺是否定的。 假設我們有:
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
。 也許在應用於錯誤類型時可能會拋出錯誤。 即使這樣的事情是可能的,但這聽起來並不是一個好主意。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.