哈斯克爾菜鳥在這里。
我在這里嘗試做的一個過于簡單的案例:
test :: Int -> a
test i = i -- Couldn't match expected type ‘a’ with actual type ‘Int’. ‘a’ is a rigid type variable bound by ...
我不太明白為什么這行不通。我的意思是,Int肯定包含在 type 中a。
我真正想要實作的是:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
data EnumType = Enum1 | Enum2 | Enum3
data MyType (a :: EnumType) where
Type1 :: Int -> MyType 'Enum1
Type2 :: String -> MyType 'Enum2
Type3 :: Bool -> MyType 'Enum3
myFunc :: EnumType -> MyType 'Enum1 -> MyType any
myFunc Enum1 t = t -- Can't match type `any` with `Enum1`. any is a rigid type variable bound by ...
myFunc Enum2 _ = Type2 "hi"
myFunc Enum3 _ = Type3 True
這里發生了什么?有沒有辦法解決這個問題,或者只是你不能做的事情?
uj5u.com熱心網友回復:
對于要撰寫的 GADT 函式,標準技術是使用單例。問題是 typeEnumType的值是值級的東西,但你想通知型別系統一些東西。所以,你需要一種方法來連接型別的樣 EnumType帶值的型別 EnumType(其本身具有一種Type)。這是不可能的,所以我們作弊:我們將xkind 的型別EnumType與新型別的值連接起來SEnumType x,這樣該值就唯一地確定了x。這是它的外觀:
data SEnumType a where
SEnum1 :: SEnumType Enum1
SEnum2 :: SEnumType Enum2
SEnum3 :: SEnumType Enum3
myFunc :: SEnumType a -> MyType Enum1 -> MyType a
myFunc SEnum1 t = t
myFunc SEnum2 _ = Type2 "hi"
myFunc SEnum3 _ = Type3 True
現在a,回傳型別MyType a不僅僅是憑空捏造的;它被約束為等于輸入a從SEnumType,和模式匹配在其上SEnumType它是讓你觀察是否a是Enum1,Enum2,或Enum3。
uj5u.com熱心網友回復:
有沒有辦法解決這個問題,或者只是你不能做的事情?
恐怕這只是“你做不到的事情”。原因很容易解釋。
當你寫一個型別簽名時(以你的第一個更簡單的例子為例)
test :: Int -> a
或者,把它寫成更字面的、擴展的形式
test :: forall a. Int -> a
你是說字面上的,“對于所有人a”,這個函式可以接受一個 Int 并回傳一個 type 的值a。這很重要,因為呼叫代碼必須相信這個型別簽名,因此能夠做這樣的事情(這不是現實的代碼,但想象一下你將 的結果提供test 2給需要其中一個Char或一個的函式的情況其他型別):
test 2 :: Char
test 2 :: [Int]
test 2 :: (Double, [Char])
等等。顯然,你的功能不能與任何的這些例子的作業-但它必須能與作業的任何人,如果你給它這種型別的簽名。很簡單,您的代碼不適合該型別簽名。(也不能,除非你通過例如“欺騙” test x = undefined。)
不過,這應該不是問題——編譯器只是在保護您免于出錯,因為我確定您意識到您的代碼無法滿足這種型別簽名。以您的“真實”示例為例:
myFunc :: EnumType -> MyType Enum1 -> MyType any
盡管這會產生編譯錯誤,但您在函式中的代碼可能是正確的,問題在于型別簽名。如果您將其替換為
myFunc :: EnumType -> MyType Enum1 -> MyType Enum1
然后它會編譯(除非有任何進一步的錯誤,我還沒有檢查過),并且可能會做你想做的事。看起來您實際上并不希望能夠呼叫myFunc并讓它生成,例如MyType Int. (如果有任何機會,我建議您提出一個單獨的問題,在其中詳細說明您在此處實際需要的內容。)
uj5u.com熱心網友回復:
如前所述,您的簽名表示通用型別
myFunc :: ? a . EnumType -> MyType 'Enum1 -> MyType a
而你真正想要表達的是一種存在型別
myFunc :: EnumType -> MyType 'Enum1 -> (? a . MyType a)
Haskell 沒有類似的功能,但它確實有一些方法可以實作基本相同的功能。
GADT 和
ExistentialTypes擴展都允許表達存在項,但您需要為它們定義一個單獨的型別。data MyDynType where MyDynWrap :: MyType a -> MyDynType myFunc :: EnumType -> MyType 'Enum1 -> MyDynType myFunc Enum1 t = MyDynWrap t myFunc Enum2 _ = MyDynWrap $ Type2 "hi" myFunc Enum3 _ = MyDynWrap $ Type3 True也許你甚至不需要一個單獨的型別,但可以簡單地修改
MyType為“動態”。data MyType = Type1 Int | Type2 String | Type3 Bool myFunc :: EnumType -> MyType -> MyType myFunc Enum1 (Type1 i) = Type1 i myFunc Enum2 _ = Type2 "hi" myFunc Enum3 _ = Type3 True通過展開一層繼續傳遞樣式,然后通過 RankNTypes 擴展使用雙全稱量詞,可以在現場匿名模擬存在。
{-# LANGUAGE RankNTypes #-} data MyType (a :: EnumType) where ... -- as original myFunc :: EnumType -> MyType 'Enum1 -> (? a . MyType a -> r) -> r myFunc Enum1 t q = q t myFunc Enum2 _ q = q (Type2 "hi") myFunc Enum3 _ q = q (Type3 True)
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/394425.html
標籤:哈斯克尔
