2014-02-08 1 views
1

Это мое определение функции любого»Почему я получаю эту ошибку, когда пытаюсь написать определение типа локальной функции в Haskell?

any' :: (t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
     where step :: Bool -> t -> Bool 
      step b x | f x  = True 
         | otherwise = b 

Я получаю эту ошибку при загрузке в объятиях:

ERROR "folds.hs":65 - Inferred type is not general enough 
*** Expression : step 
*** Expected type : Bool -> a -> Bool 
*** Inferred type : Bool -> _7 -> Bool 

... и это в GHCI:

folds.hs:65:27: 
    Couldn't match expected type `t' with actual type `t1' 
     `t' is a rigid type variable bound by 
      the type signature for any' :: (t -> Bool) -> [t] -> Bool 
      at folds.hs:62:9 
     `t1' is a rigid type variable bound by 
      the type signature for step :: Bool -> t1 -> Bool at folds.hs:64:22 
    In the first argument of `f', namely `x' 
    In the expression: f x 
    In a stmt of a pattern guard for 
        an equation for `step': 
     f x 

Когда я удалить определение типа шага. Он отлично работает, поэтому мой вопрос ... Существует способ правильно написать это определение типа или я имею дело с одним из тех, кто сидит где локальные функции не могут быть явно введены?

+1

Вам нужно расширение переменных типа области действия, иначе компилятор видит вторую t как новую переменную типа полиморфного типа, а не то же самое, что и в объявлении основного типа. –

+1

Кстати, почему foldl 'и не foldr? –

+0

Вы правы Sassa NF, foldr лучше, так как позволяет такие вещи, как «any» even (repeat 2) » – matiascelasco

ответ

3

t в подписи

where step :: Bool -> t -> Bool 

не то же самое, что и t происходит в подписи any'. Вместо этого он интерпретируется как переменная нового типа, которая является локальной для step.

Другими словами, ваш код фактически эквивалентно

any' :: (t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
    where step :: Bool -> a -> Bool   -- t renamed to a 
     step b x | f x  = True 
        | otherwise = b 

и компилятор затем жалуется, потому что step претензии в его подписи, чтобы быть применимым к любому типу a в то время как F требует t.

Возможное исправление устраняет подпись step. В этом случае компилятор сам выберет правильный тип. Это не совсем приятно с точки зрения программиста, так как компилятор теперь не будет проверять, что подпись действительно та, которую намеревался программист.

Другое исправление, как предлагается в комментариях, заключается в том, чтобы включить расширение Haskell, которое позволяет нам писать наш тип.

{-# LANGUAGE ScopedTypeVariables #-} 
import Data.List 

any' :: forall t. (t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
     where step :: Bool -> t -> Bool 
      step b x | f x  = True 
         | otherwise = b 

Здесь явное квантор forall t сообщает компилятор, что t происходящие внутри определения any' действительно то же самое t, а не новый переменный типа.

+0

спасибо! отличный ответ. Знаете ли вы, есть ли недостатки в использовании этого расширения? Я предполагаю, что есть, иначе он будет включен по умолчанию. – matiascelasco

+1

@matiascelasco нет недостатка, но за то, что компилятор должен его поддерживать. GHC делает. – chi

+0

Является ли явный 'forall' действительно необходимым здесь? Я не думал, что это было ... – MathematicalOrchid

1

Требуется расширение переменных типа области действия, иначе компилятор видит вторую t как новую переменную типа полиморфного типа, а не те же t, что и в описании основного типа.

Подсказка, что это происходит, находится в сообщении Couldn't match expected type 't' with actual type 't1'. Ясно, что ghci переименовал второй tt1, потому что он не считает их одинаковыми.

Вы можете использовать ScopedTypeVariables и явным образом forall, чтобы довести t до области внутренней функции.

{-# LANGUAGE ScopedTypeVariables #-} 

any' :: forall t.(t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
     where step :: Bool -> t -> Bool 
      step b x | f x  = True 
         | otherwise = b