簡體   English   中英

Haskell:為什么GHC不使用fundeps來推斷這個類型類中的類型?

[英]Haskell: Why does GHC not infer type in this typeclass with fundeps?

我正在嘗試使用類型類和函數依賴項來獲取一個類型函數 ,可以在下面的代碼中將Cont IntInt轉換為Cont Int ,然后在另一個類型類中使用它,如下所示。

{-# LANGUAGE KindSignatures, FunctionalDependencies, FlexibleInstances, FlexibleContexts #-}

newtype TestData a b = TestData b
newtype Cont a = Cont a

class TypeConv (repr :: * -> *) a b | repr a -> b where
class Lift repr a where
    liftOp :: (TypeConv repr a a') => a -> repr a'

instance TypeConv (TestData a) Int (Cont Int) where

instance Lift (TestData a) Int where
    liftOp i = TestData (Cont i)

這是來自ghci 7.4.2的錯誤

src/Test.hs:13:26:
    Could not deduce (a' ~ Cont Int)
    from the context (Full (TestData a) Int a')
      bound by the type signature for
                 liftOp :: Full (TestData a) Int a' => Int -> TestData a a'
      at src/Test.hs:13:5-32
      a' is a rigid type variable bound by
         the type signature for
           liftOp :: Full (TestData a) Int a' => Int -> TestData a a'
         at src/Test.hs:13:5
    In the return type of a call of `Cont'
    In the first argument of `TestData', namely `(Cont i)'
    In the expression: TestData (Cont i)

鑒於TypeConv類型類有一個fundep我讀作:“給定repra ,我們可以推斷b ”並為Int提供了一個實例,為什么ghc不能推斷a' ~ Cont Int

如果你想要一個類型函數,請使用Type Families - 這就是它們的用途。 家庭類型很容易,做你期望的。

通常編譯器不推斷您的類型的原因是您指定了函數依賴(邏輯關系)而不是函數(計算工具)。 使用fundeps是出了名的反直覺,部分原因是你在類型級別進行邏輯編程,同時在價值級別進行函數式編程。 開關! 使用類型級別的函數,具有可愛的Type Families擴展。 配有免費的lambda冰箱磁鐵,只有四個令牌(不包括p&p)。


我不確定你想要達到的目的,但這里有一個例子 - 如果我朝錯誤的方向前進,請糾正我。 你需要

{-# LANGUAGE TypeFamilies #-}

然后我們可以定義一個包含本地類型同義詞的類, TypeConv是我們的類型函數:

class Lift a where
    type TypeConv a
    liftOp :: a -> TypeConv a

然后我們可以做一個實例

instance Lift Int where
    type TypeConv Int = TestData (Cont Int)
    liftOp i = TestData (Cont i)

如果我們只想包裝Cont ,我們可以做到

instance Lift Integer where
    type TypeConv Integer = Cont Integer
    liftOp i = Cont i

你可以發瘋了

instance Lift Char where
    type TypeConv Char = [String]
    liftOp c = replicate 4 (replicate 5 c)

這讓你有

*Main> liftOp (5::Int)
TestData (Cont 5)

*Main> liftOp (5::Integer)
Cont 5

*Main> liftOp '5'
["55555","55555","55555","55555"]

安德魯對fundeps肯定是不必要的批評,類型函數更容易,但功能依賴通常提供額外的靈活性。 在這種情況下,您只需接受更長的類定義

{-# LANGUAGE KindSignatures, 
             FunctionalDependencies, 
             FlexibleInstances, 
             FlexibleContexts #-}

newtype TestData a b = TestData b
newtype Cont a = Cont a

class TypeConv (repr :: * -> *) a b | repr a -> b
class TypeConv repr a b => Lift repr a b | repr a -> b where
    liftOp :: a -> repr b

instance TypeConv (TestData a) Int (Cont Int)

instance Lift (TestData a) Int (Cont Int) where
    liftOp i = TestData (Cont i)

當然,基於類型函數的方法確實看起來更好,並且可能更可取。

暫無
暫無

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

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