2013-07-15 2 views
2

Давайте рассмотрим следующий пример:Неполного умозаключения неявного типа в Haskell

data A = A{x::Int} deriving(Show) 

instance Func_f (A -> String) where 
    f _ = "ala" 

class Func_f a where 
    f :: a 

main :: IO() 
main = do 
    let 
     a = A 5 
     x = f a 
    print 5 

скомпилирован с ghc -XFlexibleInstances main.hs

(я пробовал -XExtendedDefaultRules, но без какого-либо прогресса)

Почему во время компиляции мы получаем ошибка:

main.hs:25:21: 
    No instance for (Func_f (A -> t0)) arising from a use of `f' 
    The type variable `t0' is ambiguous 
    Possible fix: add a type signature that fixes these type variable(s) 
    Note: there is a potential instance available: 
     instance Func_f (A -> String) -- Defined at main.hs:7:10 
    Possible fix: add an instance declaration for (Func_f (A -> t0)) 
    In the expression: f a 
    In an equation for `x': x = f a 
    In the expression: 
     do { let a = A 5 
       x = f a; 
      print 5 } 

Существует только один в позиция для Func_f, поэтому Haskell должен знать результат x = f a. Вы можете исправить ошибку, указав тип вручную, например: x = f a :: String, но это не подходит для моего случая, потому что я генерирую код Haskell, и мне бы хотелось, чтобы тип Haskell's inferencer выполнял эту работу для меня.

+1

Хммм ... Только один экземпляр? Вы уверены, что вам нужен класс типа вообще? [Здесь] (http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/) вы можете прочитать, как заменить класс типа структурой данных. (Вы не выполняете экзистенциальный антивирус, но разрешение там может работать.) – AndrewC

+0

У меня было бы много экземпляров, но все они были бы одинаковыми. –

ответ

13

Вы работаете в open world assumption. Основная идея заключается в том, что GHC всегда предполагает, что вы могли бы добавить в код дополнительные экземпляры классов. Более того, вы не можете контролировать, как экземпляры будут экспортироваться и импортироваться между модулями. Поэтому, полагаясь только на то, что один экземпляр определенного класса не будет работать и приведет к странным ошибкам.

По существу, ничто не мешает вам - или кто-нибудь еще, в этом отношении - от написания другого примера, как:

instance Func_f (A -> Int) where 
    f _ = 10 

и тогда было бы невозможно понять, какой из них вы хотите в вашем коде. Это может привести к тому, что ваш код разорвется только от ссылки на другой модуль!

Однако, если вы действительно использовали это значение, скорее всего, его тип будет ограничен некоторым другим параметром, и двусмысленность исчезнет. Например, следующие работы:

main :: IO() 
main = do 
    let a = A 5 
     x = f a 
    putStr x 

В принципе, это один из тех случаев (по аналогии с read . show), где тип подписи является просто неизбежным из-за того, как GHC лечит экземпляров класса типов.

+0

Можем ли мы попросить Haskell опустить это правило в этом конкретном примере? Я знаю, что это нормально было бы плохо, но я генерирую код Haskell, и я знаю, что эта ситуация не произойдет. –

+0

Хм, не совсем. Вы можете сделать это, указав подпись типа: P. –

+3

@ danilo Да, конечно. Просто замените 'f a' на' 'ala''. –

4

Существует только один экземпляр для Func_f, поэтому Haskell должен иметь возможность узнать результат х = F A

Это нарушает the open world assumption.