2015-12-23 62 views
2

試圖創造一個Vector類型類上我碰到的元組工作一些問題無法使用實際類型匹配預期類型。該類型變量是模糊

{-# LANGUAGE TypeFamilies, FlexibleInstances #-} 

class Vector v where 
    type Scalar v :: * 
    vplus :: v -> v -> v 
    vmult :: v -> Scalar v -> v 
    vdot :: v -> v -> Scalar v 

instance Num a => Vector (a, a) where 
    type Scalar (a,a) = a 
    (a, b) `vplus` (c, d) = (a + c, b + d) 
    (a, b) `vmult` m = (a * m, b * m) 
    (a, b) `vdot` (c, d) = a * c + b * d 

的問題是,我需要明確GHC類型聲明不感到困惑。這當然是一個小小的不便,除了vdot似乎並不想工作。

res :: Int 
res = (2, 3) `vdot` (5, 5) 
-- error: Couldn't match expected type 'Int' with actual type 'Scalar (t0, t1)' 
--  The type variables 't0', 't1' are ambiguous 

這個錯誤不會消失,如果我這樣做:

res :: Int 
res = ((2, 3) :: (Int, Int)) `vdot` (5, 5) 

但現在我們已經達到的冗長的代碼如此極端它只是不實用了境界。 Haskell應該是美麗而簡潔的;沒有明確的類型地獄

我會假設GHC能夠自己解決type Scalar (a, a) = a,但即使我完全刪除實例聲明,錯誤仍然存​​在。它甚至抱怨當Vector (Int, Int)是唯一可用的實例。

那麼這裏發生了什麼?有沒有辦法讓這個工作很好?

回答

3

res :: Int 
res = (2, 3) `vdot` (5, 5) 

什麼力量23Int,甚至是同一類型的對方。所以,Vector (a, a)實例可能不適用。對於所有的GHC知道,你可能打算編寫另一個實例Vector (Float, Double)type Scalar (Float, Double) = Int和一個完全不同的實現vdotres仍然會鍵入檢查。因此,GHC告訴你,23的類型是不明確的。

這聽起來像你真正想說:一對(a, b)可以只有Vector實例時b是同一類型a(然後使用你寫的實例)。你可以表達在GHC如下:

instance (a ~ b, Num a) => Vector (a, b) where 
    type Scalar (a,b) = a 
    (a, b) `vplus` (c, d) = (a + c, b + d) 
    (a, b) `vmult` m = (a * m, b * m) 
    (a, b) `vdot` (c, d) = a * c + b * d 

a ~ b是相等的約束斷言兩種ab是相同的。現在

你的榜樣res可以正常工作:

*Main> (2, 3) `vdot` (5, 5) :: Int 
25 

這裏是推理這意味着類型不再是模糊的。

  • vdot有類型Vector v => v -> v -> Scalar v。因此,對於res進行類型檢查,我們需要找到v,這樣(2, 3) :: v,(5, 5) :: vScalar v ~ Int

  • (2, 3)有一種形式(a, b)的類型,並且有一個頭的形式爲Vector (a, b)的實例。所以,我們必須使用那個實例。(在您的原始程序中,我們無法完成此步驟,因爲沒有足夠的一般實例。)

  • 該實例的關聯類型定義告訴我們Scalar (a, b) ~ a。但我們知道Scalar (a, b)應該是Int,所以我們必須有a ~ Int

  • 該實例的限制告訴我們a ~ b並且應該有一個實例Num a。所以,我們也必須有b ~ Int(實際上Num Int成立)。

  • 所以,我們的結論是,2 :: Int3 :: Int,自(5, 5) :: v此外,我們還5 :: Int5 :: Int了。

  • 現在,我們已經確定使用哪種類型的類對我們表達(2355vdot)每重載名,因此不存在歧義,我們終於可以計算表達式。

+0

我想如果它說'標量(a,a)'和'向量(a,a)'唯一可用的類型是'a'。所以如果'a'是一個Int,其他每個'a'的實例都必須是Int –

+0

我想你只是假設你的'Vector(a,a)'實例將被選中。關鍵是沒有任何東西會強制它被選中,事實上,你可以添加第二個不重疊的實例,如果在'res'中使用,也會進行類型檢查。 GHC從不「只是假設」一個實例將被選中,只是因爲它具有實例可用,它需要使用我的答案的後半部分概述的邏輯來查找實例。 –

+0

但是如果它是唯一可用的實例,那麼它怎麼不一定選擇那個實例呢?是不是有一個方形的洞和一個不能進去的正方形,因爲它可能是一個圓圈? –

1

讓我們簡化事項:

class Vector v where 
    type Scalar v :: * 
    vdot :: v -> v -> Scalar v 
    ...  
instance Num a => Vector (a, a) where 
    type Scalar (a,a) = a 
    ... 
res :: Int 
res = (2, 3) `vdot` (5, 5) 

現在,我們有

vdot :: v  -> v -> Scalar v 
vdot (2,3) (5,5) 

這樣的雙重應用程序必須有這種類型的

(2,3) :: v 
(5,5) :: v 
res = vdot (2,3) (5,5) :: Scalar v 

擴大的類型配對:

(2,3) :: (a1, a2) ~ v  for some a1, a2 in class Num 
(5,5) :: (b1, b2) ~ v  for some b1, b2 in class Num 
res = vdot (2,3) (5,5) :: Scalar v 

通過及物性,(a1, a2) ~ (b1, b2),因此a1 ~ b1a2 ~ b2

(2,3) :: (a1, a2)   for some a1, a2 in class Num 
(5,5) :: (a1, a2) 
res = vdot (2,3) (5,5) :: Scalar (a1, a2) 

我們也知道從

res :: Int 

因此

Scalar (a1, a2) ~ Int 

但是從這個沒有辦法知道什麼是a1, a2註釋。畢竟,人們可能會使用自定義類型:

data A1 = ... 
data A2 = ... 
instance Num A1 where ... 
instance Num A2 where ... 
instance Vector (A1, A2) where 
    type Scalar (A1, A2) = Int -- !!!! 

請注意最後的Int。這導致兩者都使

type Scalar (Int, Int) = Int 
type Scalar (A1 , A2) = Int 

使得不可能選擇a1, a2的類型。這通常由句子「類型家族不必是內射」提到。

另請注意,代碼中缺少instance Vector (A1, A2)無助於此。 GHC必須編譯你的代碼,期望這個實例稍後被聲明,可能在其他模塊中(「開放世界」假設)。這非常需要允許單獨編譯。

相關問題