2013-04-13 4 views
12

Я делаю некоторые excersises, где я должен добавить тип функции и объяснить, что она делает. Я застрял с этим:

phy = uncurry ($) 

типа, в соответствии с GHCi является phy :: (a -> b, a) -> b. Знание моего haskell является основным, поэтому я действительно не знаю, что он делает.

+0

Вот милая мысль: 'uncurry ($)' на самом деле то же самое, что 'uncurry id'! Это происходит потому, что '($)' на самом деле просто 'id' специализируется на функциях. Попробуйте ': t uncurry id', чтобы понять, что я имею в виду. –

+4

Этот код иллюстрирует силу мышления в типах. Выяснение того, что 'uncurry ($)' действительно задумывается, но должно быть очевидно, что делает функция типа '(a -> b, a) -> b'. На самом деле, есть только одна вещь, функция с этим типом * может * делать. Вы должны развивать эту интуицию естественным образом, если обратить внимание на типы, которые вы программируете в Haskell. Если вы позже захотите формализовать свою интуицию, попробуйте статью «[Теоремы бесплатно] (http://ttic.uchicago.edu/~dreyer/course/papers/wadler.pdf)» [PDF] by Philip Wadler – pash

ответ

9
uncurry :: (a -> b -> c) -> (a, b) -> c 

($) :: (a -> b) -> a -> b 

uncurry ($) :: (a -> b, a) -> b 

Если вы проверяете типы uncurry и $ и его описание:

uncurry преобразует каррированной функцию в функцию по парам.

Все, что он делает, это функция (a -> b -> c) и возвращает функцию, которая принимает параметры как кортеж.

Так phy делает то же самое, как $, но вместо f $ x или ($) f x вы называете это как phy (f, x).

+0

Я получаю это сейчас, спасибо. – user2278354

+0

Я думаю, было бы еще яснее, если бы вы использовали те же переменные типа в сигнатуре типа для 'uncurry ($)'. –

+0

Вы правы, я только что скопировал из ghci – Arjan

12

Давайте систематически изложим типовую деталь. Мы начнем с типами uncurry и ($):

uncurry :: (a -> b -> c) -> (a, b) -> c 
($)  :: (a -> b) -> a -> b 

Поскольку целевое выражение имеет ($) в качестве аргумента uncurry, давайте выстраиваться их типами, чтобы отразить это:

uncurry :: (a  -> b -> c) -> (a, b) -> c 
($)  :: (a -> b) -> a -> b 

Всего типа из ($) строки с первым типом аргумента uncurry, а аргументы и типы результатов ($) совпадают с первым аргументом uncurry, как показано. Это соответствие:

uncurry's a <==> ($)'s a -> b 
uncurry's b <==> ($)'s a 
uncurry's c <==> ($)'s b 

Это своего рода сбивает с толку, потому что a и b переменные типа в одном типе не являются такими же, как в другом (так же, как x в plusTwo x = x + 2 не то же самое, как x в timesTwo x = x * 2). Но мы можем переписать типы, чтобы помочь разобраться в этом. В простых сигнатурах типа Haskell, подобных этому, в любое время, когда вы видите переменную типа, вы можете заменить все ее вхождения каким-либо другим типом, также получить допустимый тип. Если вы выберете свежие переменные типа (введите переменные, которые не отображаются нигде в оригинале), вы получите эквивалент (тот, который можно преобразовать обратно в исходное); если вы выберете неповторимый тип, вы получите специальную версию оригинала, которая работает с более узким диапазоном типов.

Но в любом случае, давайте применим это к типу uncurry ::

-- Substitute a ==> x, b ==> y, c ==> z: 
uncurry :: (x -> y -> z) -> (x, y) -> z 

Давайте переделывать "линию", используя переписанный тип:

uncurry :: (x  -> y -> z) -> (x, y) -> z 
($)  :: (a -> b) -> a -> b 

Теперь это очевидно: x <==> a -> b, y <==> a и z <==> b.Теперь, подставляя переменные типа uncurry «s для своих типов контрагентов в ($), мы получаем:

uncurry :: ((a -> b) -> a -> b) -> (a -> b, a) -> b 
($)  :: (a -> b) -> a -> b 

И, наконец:

uncurry ($) :: (a -> b, a) -> b 

Так вот, как вы выясните тип. Как насчет того, что он делает? Ну, лучший способ сделать это в этом случае - посмотреть на тип и подумать об этом внимательно, выяснить, что нам нужно написать, чтобы получить функцию этого типа. Давайте перепишем это так, чтобы сделать его более загадочным:

mystery :: (a -> b, a) -> b 
mystery = ... 

Поскольку мы знаем mystery является функцией одного аргумента, мы можем расширить это определение, чтобы отразить это:

mystery x = ... 

Мы также знаем, что его аргумент является пара, так что мы можем расширить немного больше:

mystery (x, y) = ... 

Поскольку мы знаем, что x является функцией и y :: a, я лик е использовать f означает «функцию» и называть переменные такие же, как их типа, это помогает мне причину о функциях, так давайте сделаем это:

mystery (f, a) = ... 

Теперь, что мы помещаем в правой части ? Мы знаем, что он должен быть типа b, но мы не знаем, какой тип b (это вообще то, что выбирает абонент, поэтому мы не можем знать). Поэтому мы должны как-то сделать b, используя нашу функцию f :: a -> b и значение a :: a. Ага! Мы можем просто вызвать функцию со значением:

mystery (f, a) = f a 

Мы написали эту функцию, не глядя на uncurry ($), но оказывается, что он делает то же самое, как uncurry ($) делает, и мы можем доказать это. Давайте начнем с определений uncurry и ($):

uncurry f (a, b) = f a b 
f $ a = f a 

Теперь, подставляя равно для равных:

uncurry ($) (f, a) = ($) f a   -- definition of uncurry, left to right 
        = f $ a   -- Haskell syntax rule 
        = f a    -- definition of ($), left to right 
        = mystery (f, a) -- definition of mystery, right to left 

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

4

Остальные два ответа в порядке. У меня просто немного другое дело.

uncurry :: (a -> b -> c) -> (a, b) -> c 
($)  :: (a -> b) -> a -> b 

С «->» в типа подписи правоассоциативных, я могу что то же записать эти две подписи типа, как это:

uncurry :: (a -> b -> c) -> ((a, b) -> c) 
($)  :: (a -> b) -> (a -> b) 

uncurry принимает произвольную функцию двух входов и изменяет его в funciton одного аргумента, где этот аргумент является кортежем исходных двух аргументов.

($) принимает простую функцию с одним аргументом и превращает ее в ... сам. Его единственный эффект является синтаксическим. f $ эквивалентен f.

2

(Убедитесь, что вы понимаете функции высшего порядка и каррирование, читать узнать, что Вы в Haskell главу о higher-order functions, а затем прочитать difference between . (dot) and $ (dollar sign) и function composition (.) and function application ($) idioms)

($) это просто функция приложения, f $ x эквивалентно f x. Но это хорошо, потому что мы можем использовать явное применение функции, например:

map ($2) $ map ($3) [(+), (-), (*), (**)] -- returns [5.0,1.0,6.0,9.0] 

что эквивалентно:

map (($2) . ($3)) [(+), (-), (*), (**)] -- returns [5.0,1.0,6.0,9.0] 

Проверьте тип ($): ($) :: (a -> b) -> a -> b. Вы знаете, что объявления типа являются право-ассоциативными, поэтому тип ($) также может быть записан как (a -> b) -> (a -> b). Подожди секунду, что это? Функция, которая получает унарную функцию и возвращает унарную функцию того же типа? Это похоже на конкретную версию функции идентификации id :: a -> a. Хорошо, некоторые типы первых:

($) :: (a -> b) -> a -> b 
id :: a -> a 
uncurry :: (a -> b -> c) -> (a, b) -> c 
uncurry ($) :: (b -> c, b) -> c 
uncurry id :: (b -> c, b) -> c 

При кодировании Haskell, всегда обращайте внимание на типы, они дают вам много информации, прежде чем вы даже смотреть на код. Итак, что такое ($)? Это функция из 2 аргументов. Что такое uncurry? Это тоже функция из 2 аргументов, первая из которых является функцией из двух аргументов. Таким образом, uncurry ($) должен иметь вид typecheck, потому что 1 st аргумент uncurry должен быть функцией от 2 аргументов, которые ($) есть. Теперь попробуйте угадать тип uncurry ($). Если выбран тип ($) «ы это (a -> b) -> a -> b, заменить его на (a -> b -> c): a становится (a -> b), b становится a, c становится b, поэтому, uncurry ($) возвращает функцию типа ((a -> b), a) -> b. Или (b -> c, b) -> c, как указано выше, что то же самое. Так что говорит нам этот тип? uncurry ($) принимает кортеж (function, value). Теперь попробуйте угадать, что это делает только с типом.

Теперь, перед ответом, интерлюдия. Haskell так strongly typed, что он запрещает возвращать значение конкретного типа, если объявление типа имеет переменную типа в качестве типа возвращаемого значения. Поэтому, если у вас есть функция с типом a -> b, вы не можете вернуть String. Это имеет смысл, потому что если тип вашей функции был a -> a, и вы всегда возвращали String, как бы пользователь мог передать значение любого другого типа? Вы должны либо иметь тип String -> String, либо иметь тип a -> a и возвращать значение, зависящее исключительно от входной переменной. Но это ограничение также означает, что невозможно написать функцию для определенных типов. Нет функции с типом a -> b, потому что никто не знает, какой конкретный тип должен быть вместо b. Или [a] -> a, вы знаете, что эта функция не может быть total, потому что пользователь может передать пустой список и что будет возвращать функция в этом случае? Тип a должен зависеть от типа внутри списка, но в списке нет «внутри», его пустое, поэтому вы не знаете, каков тип элементов внутри пустого списка.Это ограничение допускает только для очень узкой локтевой комнаты для возможных функций под определенным типом, и именно поэтому вы получаете так много информации о возможном поведении функции, просто читая тип.

uncurry ($) возвращает что-то типа c, но это переменная типа, а не конкретный тип, поэтому его значение зависит от того, что также относится к типу c. И мы видим из объявления типа, что функция в кортеже возвращает значения типа c. И та же функция запрашивает значение типа b, которое может быть найдено только в одном кортеже. Там нет каких-либо конкретных типов, ни классов типов, так что единственное uncurry ($) может сделать, это взять snd кортежа, положить его в качестве аргумента в функции в fst кортежа, вернуть то, что он возвращает:

uncurry ($) ((+2), 2) -- 4 
uncurry ($) (head, [1,2,3]) -- 1 
uncurry ($) (map (+1), [1,2,3]) -- [2,3,4] 

Там представляет собой милую программу djinn, которая генерирует программы Haskell на основе типов. Играть с ним, чтобы увидеть, что наш тип догадок функциональности uncurry ($) корректна:

Djinn> f ? a -> a 
f :: a -> a 
f a = a 
Djinn> f ? a -> b 
-- f cannot be realized. 
Djinn> f ? (b -> c, b) -> c 
f :: (b -> c, b) -> c 
f (a, b) = a b 

Это показывает также, что fst и snd являются только те функции, которые могут иметь свои соответствующие типы:

Djinn> f ? (a, b) -> a 
f :: (a, b) -> a 
f (a, _) = a 
Djinn> f ? (a, b) -> b 
f :: (a, b) -> b 
f (_, a) = a 

 Смежные вопросы

  • Нет связанных вопросов^_^