2015-03-03 60 views
2

我有一個功能foo與一系列的約束。當然,這些約束必須出現在使用foo的函數的簽名中,所以我試圖做的是將foo約束包含在類型同義詞FooCtx a b ... :: Constraint中。作爲一個例子,利用fundeps綁定約束

foo :: (A a, B b, C c, ...) => a -> b -> c 

bar :: (A a, B b, C c, ...) ... 
bar = ... foo ... 

將成爲

type FooCtx a b c ... = (A a, B b, C c, ...) 
foo :: (FooCtx a b c) => a -> b -> c 
bar :: (FooCtx a b c) => ... 

如果所有類型的暴露這個偉大的工程。但是,我正在使用函數依賴來生成約束列表中的某些類型,並且這些類型不會出現在foo的簽名中。例如:

class Bar a b | a -> b 

foo (Bar a b, ...) => a -> a 

GHC不會接受type FooCtx a = (Bar a b)因爲b未綁定的LHS。我也不能使用type FooCtx a b = (Bar a b),因爲b不在foo的簽名範圍內。 foo的簽名應爲foo :: (FooCtx a ?) => a -> a

一個不能令人滿意的解決辦法是把制約因素之一在foo簽名與FooCtx帶來fundep類型中範圍:

class Bar a b | a -> b 

type FooCtx a b = ... 

foo (Bar a b, FooCtx a b) => a -> a 

但這違背了分組的約束的目的:

在遇到這種情況之前,我認爲約束同義詞可以被盲目取代任意約束列表。我知道封裝這樣的約束的唯一方法是使用一個類,但它遭受同樣的問題:class (A a, B b, C c, ...) => FooCtx a b c在LHS上不能有任何隱藏類型。有沒有其他方法可以完全收集所有這些限制?

+0

「type FooCtx a b = ...'出現了什麼問題 - 這隻比'type FooCtx a = ...'長2個字符。 – user2407038 2015-03-03 04:16:56

+0

正如我上面提到的,GHC接受你的簽名,但對於'foo'的LHS我沒用,因爲'b'不在'foo'的簽名範圍內。 – crockeea 2015-03-03 04:18:58

+0

是否有任何理由不能使用'TypeFamilies'而不是'FunctionalDependencies'? – Cirdec 2015-03-03 04:59:14

回答

5

你誤解了類型變量是如何綁定的。它們是而不是,由tau類型(在本例中爲a -> a)綁定,但是基於完整phi類型的隱式綁定器((Bar a b) => a -> a)。使用GHC語言擴展可以明確該綁定。

在您的例子,當你喜歡寫東西

foo :: (Bar a b) => a -> a 

然後充分σ型,具有明確tyvar結合拼出來的,是下面的(因爲在隱式情況下,從披類型的所有tyvars這裏必然)

foo :: forall a b. (Bar a b) => a -> a 

這意味着在使用約束別名以同樣的方式沒有問題:如果你有如

type FooCtx a b = (Bar a b, Num a, Eq a) 

再下面是一個有效的類型簽名:

foo' :: forall a b. (FooCtx a b) => a -> a 

,因此,以下的簡寫是有效的,以及:

foo' :: (FooCtx a b) => a -> a 
+0

有趣!謝謝。 – crockeea 2015-03-03 05:23:02

1

這個問題可以通過TypeFamiliesFlexibleContexts來解決。

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE FlexibleContexts #-} 

我們有三個班,ABC和你原來的foo功能。

class A a 
class B a 
class C a 

foo :: (A a, B b, C c) => a -> b -> c 
foo = undefined 

Bar類使用類型的家庭弄清楚B去與一個a。爲了編寫示例foo',我添加了一個附加功能。

class Bar a where 
    type BofA a :: * 
    aToB :: a -> BofA a 

foo'是沒有是B任何輸入或輸出的功能,但在其實現仍然使用foo。它要求與a關聯的BofA類型滿足B約束。這個簽名需要靈活的上下文。

foo' :: (A a, Bar a, B (BofA a), C c) => a -> a -> c 
foo' x y = foo x (aToB y)