2016-08-29 2 views
7

Я новичок в Haskell, и я наткнулся на функцию (.)(.), я использую :t, чтобы получить его тип GHCi:Как понять функцию «(.) (.)» В Haskell

:t (.)(.) 
(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c 

Как понять тип (.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c здесь? Я очень смущен.

+1

Я думаю, что есть дубликат. Связанный https://stackoverflow.com/questions/16202289/haskell-function-composition-type-of-and-how-its-presented? Но на самом деле это не объясняет тип. – Zeta

+1

Должно быть закрыто. Причина: Porn. – Damiano

ответ

2

Это частичное применение оператора композиции к самому составу.В общем, мы знаем, что если мы применим (.) к некоторой функции f :: x -> y, то

>>> :t (.) f 
(.) f :: (a -> x) -> a -> y 

из-за того, как типы выстраиваться:

(b -> c) -> (a -> b) -> a -> c 
x -> y 
-------------------------------- 
      (a -> x) -> a -> y 

Мы бросаем первый аргумент, и заменить остальные вхождения b и c с соответствующими типами данного аргумента.

Здесь f всего лишь (.), что означает, что мы идентифицируем x ~ (b -> c) и y ~ (a -> b) -> a -> c. Подкладка типов снова

(a -> x ) -> a ->    y 
     b -> c    (a -> b) -> a -> c 

Поскольку a происходит на верхнем и нижний, мы должны выбрать новое переменное имя a на дне; GHC выбрал a1:

(a -> x ) -> a ->    y 
     b -> c    (a1 -> b) -> a1 -> c 

Ввод двух вместе дает тип, который вы видите GHCi.

(a -> b -> c) -> a -> (a1 -> b) -> a1 -> c 

Anatomy шутки в сторону, то, что является(.)(.)?

Предположим, у вас есть функция f :: a -> b, но вы хотите функцию g :: a -> c, то есть вы хотите f, но с другим типом возврата. Единственное, что вы можете сделать, это найти вспомогательную функцию h :: b -> c, которая преобразует возвращаемое значение для вас. Ваша функция g затем просто состав h и f:

g = h . f 

Вы могли бы, однако, имеет более общую функцию h' :: t -> b -> c, которые могут поворот значения типа b в значение типа c нескольких способов, в зависимости на значение некоторого аргумента x :: t. Тогда вы можете получить много разных g s в зависимости от этого аргумента.

g = (h' x) . f 

Теперь, учитывая h', x и f, мы можем вернуть нашу g, так что давайте напишем функцию, которая делает это: функция, которая «способствует» возвращаемое значение f из значения типа b к значение типа c, учитывая функцию h' и некоторое значение x:

promote h' x f = (h' x) . f 

Вы можете механически преобразовать любую функцию, чтобы указать, в свободной форме; Я не знаком с деталями, но с использованием PointFree.io производит

promote = ((.) .) 

который является только частичным применением (.) (.) написано в разделе, то есть:

((.) (.)) h' x f == (h' x) . f 

Итак, наш оператор «сиськи» является просто обобщенный оператор предварительной компоновки.

2

Функция (.) имеет тип (b -> c) -> (a -> b) -> (a -> c), т.е. данное две функции, одна из a к b и один из b к c, он прилипает их вместе, чтобы сформировать единый a к c функции.

Снова напиши тип (.), но используя разные буквы, чтобы отличить их: (y -> z) -> (x -> y) -> (x -> z). Предположим, что версия a b c является первой (.) в (.)(.), а версия x y z - вторая. Мы передаем второй первый аргумент первому. Помните, что первый аргумент первого имеет тип (b -> c), поэтому нам нужно сопоставить его с типом второй функции.

Вы можете заметить, что здесь есть несоответствие: (b -> c) - это функция, которая принимает один аргумент, но (.) занимает два. Но в Haskell все функции имеют значение curried, что означает, что функция, принимающая два аргумента, действительно такая же, как функция, которая принимает один аргумент и возвращает другую функцию, которая принимает один аргумент (второй из двух оригинальных), и только эта функция затем возвращает реальный результат.

Или, другими словами, конструктор типа стрелки связывается справа налево, и мы можем заключить в круглые скобки, чтобы сделать его более ясным: для наших целей тип второго (.) лучше всего писать как (y -> z) -> ((x -> y) -> (x -> z)). Сопоставьте это против (b -> c), и ясно, что это означает, что b = (y -> z) и c = ((x -> y) -> (x -> z)).

Приведенный первый аргумент представляет собой остаток от первого типа (.) с переменными типа, замененными нашими заменами. Таким образом, тип (.)(.)(a -> (y -> z)) -> (a -> ((x -> y) -> (x -> z))).

Теперь мы можем отбросить все круглые скобки, которые находятся справа от стрелки, чтобы упростить это выражение и получить (a -> y -> z) -> a -> (x -> y) -> x -> z. Легко видеть, что это точно (по модулю переименование), что дал вам GHCi.

И этот тип и функция означает, что «дал бинарную функцию b, которая принимает a и y и возвращает z, и учитывая также значение va типа a, и дали унарную функцию u, которая принимает x и возвращает y, и учитывая, наконец, значение vx типа x, дайте мне z, которая является результатом вычисления b va (u vx).

Вы, наверное, не нужны. Единственная причина, функция интересна в том, что она выглядит как сиськи

0
  ┌──────────────────────────────────────────────────────────────────────────┐ 
      │                   │ 
     ┌─────────────────────────────────────────────────┐        │ 
     │ │           │        │ 
┌─────────────────────────┐      ┌──────────────────────┐   │ 
│ │ │    │      │  │    │   │ 
↓ ↓ ↓    │      ↓  │    │   │ 
(a -> b -> c)  ->  a   ->   (a1 -> b)  ->  a1  -> c 
───────────    ───      ───────    ──   
     ↓     ↓       ↓     ↓ 
    (f)     (x)      (g)     (y) 
     ↓     ↓       ↓     ↓ 
a function  a thing that works  a function of one  a thing that 
of two arguments as the first argument argument that   works as the 
that returns  of f     returns a thing   argument of g 
the same type        suitable as the second 
(.)(.) returns       argument of f 

Теперь, как мы можем объединить эти четыре вещи?

Сначала мы можем принять f и применить его к x. Что это дает нам? Функция одного аргумента. Его тип должен быть b->c, потому что мы только что применили функцию типа a->b->c к аргументу типа a.

Затем мы можем взять второй g и применить его к y. Это дает нам что-то типа b.

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

Обратите внимание, что все это можно обнаружить, глядя только на тип. Не нужно знать, как функция была первоначально реализована вообще.

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