簡體   English   中英

Haskell 中的“模式匹配”類型

[英]"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 和實例。 所以,如果你想支持IntBool和列表,你可以這樣寫:

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給出2f '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.

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