簡體   English   中英

多態函數可以在兩種類型之間進行轉換

[英]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)

我試圖創建一些通用函數來在任何兩種類型ab之間進行轉換,但是兩者都有些困難。 然而,它們被第一個參數a ->b立即設置,所以我希望這是可能的。 任何指導表示贊賞......

謝謝!

[編輯]

我看到它的方式,我的問題歸結為haskell的類型系統是否支持or關鍵字...因為我的函數的直觀簽名是: f :: (a->b) -> (b->a) -> a -> b or (a->b) -> (b->a) -> b -> a ...

正如alecb解釋的那樣,它不能以你的簽名建議的方式完成,至少通過普通手段(它需要在運行時檢查類型)。 您可能喜歡的另一種選擇是lens中的IsoPrism 如果你的轉換函數是完全和雙射的,那么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接受IntString 這給我們留下了三個選擇:

  1. g是一個完全多態的函數 - 也就是說,它的第一個參數是a類型。 所以g Trueg [1,2,3]也是合法的。 一個如此通用的函數與轉換無關,你可以證明這種函數不能做任何有趣的事情(例如,類型a -> Int的函數必須是常量)

  2. g期望某個類型類的參數 - 例如, show是一個只接受一組受限類型(包括IntString )的函數。 但是現在你的函數不是完全多態的 - 它只適用於這個受限制的類型組。

  3. 我們可以為此任務定義新的數據類型:

     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 

    雖然這個函數在某些情況下可能很有用,但它不再是通用的(它支持的唯一對是IntString )。

所以答案是 - 您可以使用此類型簽名定義函數,但它僅適用於特定類型或類型類。

編輯:不,不支持類型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應用於IntString ,這在haskell中通常是不可能的:你可以將foo應用於其中一個而不是兩者。

編輯 :這不像整個故事 - foo當然可以是foo :: c -> d ,但這將允許它應用於任何類型,而不僅僅是IntString 也許在應用於錯誤類型時可能會拋出錯誤。 即使這樣的事情是可能的,但這聽起來並不是一個好主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM