[英]"Pattern-matching" types in Haskell
我想要一個 function f
充當Int
的增量和所有其他類型的標識。 我嘗試啟用TypeApplications
擴展並以最直接的方式執行此操作:
f :: forall a. a -> a
f @Int x = x + 1
f @_ x = x
但是擴展並沒有啟用這樣的模式:
<interactive>:6:1: error: Parse error in pattern: f @Int
Haskell 中的類型(或類似機制)是否存在模式匹配?
如前所述,這在 Haskell 中是不可能實現的,因為它會違反稱為“參數性”的參數多態性的基本屬性之一,它確保任何多態性 function 滿足稱為“自由定理”的特定屬性。
特別是,一個終止 function 類型的forall a. a -> a
forall a. a -> a
必須是身份。 沒有其他可能的實現。
話雖如此,如果我們允許對類型a
進行約束,這將成為可能。 通常,運行時類型測試是在 Haskell 中通過Typeable
類型 class 完成的:
f :: forall a. Typeable a => a -> a
f x = case eqT @a @Int of -- Is a ~ Int ?
Just Refl -> x + 1 -- If it is, we can add one.
Nothing -> x -- Otherwise, return x as-is.
這需要一堆 GHC 擴展,並導入Data.Typeable
。
有,但你不能像在計算級別那樣有一個失敗的模式。 也就是說,在計算級別,我可以寫
f 0 = 42
f n = n-1
具有完全不匹配的其他模式時的貫穿模式n
。 在類型級別,你不能這樣做——即使嵌套在其他模式中。 你可以有一個匹配任何東西的模式,但不能匹配“其他任何東西”——如果你匹配任何東西,你就不能有任何其他模式。
因此,一旦您擁有要匹配的模式集合,您就可以編寫類型 class 和實例。 所以,如果你想支持Int
、 Bool
和列表,你可以這樣寫:
class F a where f :: a -> a
instance F Int where f = succ
instance F Bool where f = id
instance F [a] where f = id
最后一個例子展示了我之前的意思,你可以有一個匹配任何東西的模式,但不能匹配“任何其他”的模式: [a]
是一個很好的實例,但它排除了[Bool]
的創建后面的實例。
在這里,您嘗試根據類型級信息生成不同的術語級代碼,因此您需要一個類型類; 並且您正在嘗試匹配類型,因此您可以使用關聯的類型 family 。 此方法需要重疊實例:
{-# Language
AllowAmbiguousTypes,
FlexibleInstances,
TypeFamilies #-}
-- The class of overloads of ‘f’.
class F a where
type family T a -- Or: ‘type T a’
f :: T a -> T a
-- The “default” catch-all overload.
instance {-# Overlappable #-} F a where
type instance T a = a -- Or: ‘type T a = a’
f x = x
-- More specific instance, selected when ‘a ~ Int’.
instance {-# Overlapping #-} F Int where
type instance T Int = Int
f x = x + 1
然后f @Int 1
給出2
, f 'c'
給出'c'
。
但是在這種情況下,類型族是不必要的,因為它恰好是同一性——我包括它是為了給出一個概括的點。 如果您想通過對類型進行模式匹配來生成類型,就像 function 一樣,那么封閉類型系列非常適合:
{-# Language
KindSignatures,
TypeFamilies #-}
import Data.Int
import Data.Word
import Data.Kind (Type)
type family Unsigned (a :: Type) :: Type where
Unsigned Int = Word
Unsigned Int8 = Word8
Unsigned Int16 = Word16
Unsigned Int32 = Word32
Unsigned Int64 = Word64
Unsigned a = a
回到您的問題,這是帶有普通類型類的原始示例:
class F a where
f :: a -> a
instance {-# Overlappable #-} F a where
f x = x
instance {-# Overlapping #-} F Int where
f x = x + 1
不幸的是,沒有“封閉類型類”的概念可以讓您避免重疊的實例。 在這種情況下,它是相當良性的,但是在更復雜的情況下可能會出現連貫性問題,尤其是在MultiParamTypeClasses
中。 一般來說,如果可能的話,最好添加一個新方法而不是編寫重疊的實例。
請注意,在任何一種情況下, f
現在都有一個F a
約束,例如后者是(F a) => a -> a
。 你無法避免類型的一些變化; 在 Haskell 中,多態意味着參數多態,以保留類型可以在編譯時擦除的屬性。
其他選項包括 GADT:
data FArg a where
FInt :: Int -> FArg Int
FAny :: a -> FArg a
f :: FArg a -> a
f (FInt x) = x + 1 -- Here we have ‘a ~ Int’ in scope.
f (FAny x) = x
或者(已經在其他答案中提到)動態類型的Typeable
約束:
{-# Language
BlockArguments,
ScopedTypeVariables #-}
import Data.Typeable (Typeable, eqT)
import Data.Type.Equality ((:~:)(Refl))
f :: forall a. (Typeable a) => a -> a
f x = fromMaybe x do
Refl <- eqT @a @Int
pure (x + 1)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.