- PVSM.RU - https://www.pvsm.ru -

Об именах в Haskell

В общем случае, имена в Haskell могут состоять из буквенно-цифровых символов. Кроме того, допускается в имени использовать символ ', а символ _ считается буквой. Первый символ в имени не может быть цифрой. Имена функций и операторов обязательно должны начинаться со строчной буквы, а имена типов данных, конструкторов данных и классов типов — с прописной. Кроме того, в ряде случаев имена могут состоять из символов набора ascSymbol. В данной заметке даётся некоторая информация об использовании символов этого набора.

Имена операторов

Согласно § 2.2 стандарта Haskell 2010 специальными (special) считаются следующие символы:

(
)
,
;
[
]
`
{
}

Там же отдельно определён и следующий набор символов, именованный как ascSymbol:

!
#
$
%
&
*
+
.
/
<
=
>
?
@

ˆ
|
-
˜
:

Буквенно-цифровые символы в стандарте выделены в отдельные наборы: ascSmall, ascLarge, uniSmall, uniLarge, ascDigit, uniDigit и т.д.

Иногда специальными ошибочно называют символы, входящие в набор ascSymbol. Например, давая определение оператору порой говорят, что их имена состоят только из специальных символов. На самом же деле специальные символы вовсе не могут использоваться в составе имён операторов (да и вообще в составе любых имён).
Операторами в Haskell называются функции, имена которых состоят только из символов набора ascSymbol, не начинаются с символа: и при этом такие функции должны принимать строго два параметра. Все операторы являются функциями, но не все функции являются операторами. В именах Haskell нельзя смешивать символы из набора
ascSymbol и из других наборов. Т.е. имя должно формироваться либо только из символов набора ascSymbol,
либо только из других символов, допустимых к использованию в именах, но не входящих в набор ascSymbol.

Имена операторов могут использоваться как в инфиксной, так и в префиксной формах записей, причём не только в процессе использования оператора в основном коде, но и в самом коде его определения. В случае использования префиксной формы, имя оператора обязательно нужно заключать в круглые скобки. В сигнатуре оператора его имя так же обязательно заключать в круглые скобки. Следующие два определения оператора идентичны:

Пример 1

(!#$%&*+./<=>?@^|-~:) :: Int -> Int -> Int -- Сигнатура оператора
a !#$%&*+./<=>?@^|-~: b = a * b -- Определение оператора в инфиксной форме
  

Пример 2

(!#$%&*+./<=>?@^|-~:) :: Int -> Int -> Int -- Сигнатура оператора
(!#$%&*+./<=>?@^|-~:) a b = a * b -- Определение оператора в префиксной форме
  

Примеры вызова оператора в консоли ghci в инфиксной и префиксной формах:

λ: 10 !#$%&*+./<=>?@^|-~: 20
200
λ: (!#$%&*+./<=>?@^|-~:) 40 20
800

Согласно документации Haskell [1] тот момент, когда оператор вызывается в префиксной форме, он воспринимается не как оператор, но как обычная функция.

Разрешено создавать дополнительные операторы, присваивая им произвольные имена за исключением следующих, зарезервированных имён:

..
:
::
=

|
<-
->
@
~
=>

Функция, имя которой состоит только из символов набора ascSymbol, но при этом имеющая количество параметров не равное двум — не является оператором. В коде примеров 3 и 4 показаны такие функции.
Пример 3

(!#$%&*+./<=>?@^|-~:) :: Int -> Int -- Функция с одним параметром
(!#$%&*+./<=>?@^|-~:) a = a * a

Пример вызова функции в консоли ghci:

λ: (!#$%&*+./<=>?@^|-~:) 5
25

Пример 4

(!#$%&*+./<=>?@^|-~:) :: Int -> Int -> Int -> Int -- Функция с тремя параметрами
(!#$%&*+./<=>?@^|-~:) a b c = a + b + c

Примеры вызова функции в консоли ghci:

λ: (!#$%&*+./<=>?@^|-~:) 3 5 6
14
λ: (3 !#$%&*+./<=>?@^|-~: 5) 6
14

В последней строке показан способ инфиксного вызова функции, принимающей три параметра. Вообще, инфиксным способом можно вызывать любую функцию, количество параметров которой больше одного, например:

((+) `foldr` 0) [1..10]

Данный код возвращает число 50.

Имена конструкторов данных

Как уже отмечалось выше — имена конструкторов данных начинаются с прописной буквы, состоит из буквенно-цифровых символов, а так же символов _ и ' (при необходимости). Однако допускается и система наименований, схожая с той, которая
применяется по отношению к операторам...

Стандартом Haskell разрешается формировать имена конструкторов данных из символов набора ascSymbol. Подобно операторам, такие конструкторы могут использоваться как в инфиксной, так и в префиксной форме. Кроме того, их имена обязательно должны начинаться с символа: (двоеточие). Т.е. если вы где-то в коде увидели нечто вроде 123 :#$% "ASDF", то сразу же можете быть уверенными в том, что перед вами вызов конструктора :#$% с параметрами 123 и «ASDF».
Пример 5

data Symbolic n
  = Constant n
   | Variable String
   | Symbolic n :=> Symbolic n
   | Symbolic n :<= Symbolic n
   | (:<=>) (Symbolic n) (Symbolic n)
  deriving Show

Cоздадим в ghci несколько экземпляров типа Symbolic, используя разные конструкторы данных:

λ: let a = Constant 10; b = Variable "Hello"
λ: let n = a :=> b; m = a :<= b; k = a :<=> b
λ: n
Constant 10 :=> Variable "Hello"
λ: m
Constant 10 :<= Variable "Hello"
λ: k
(:<=>) (Constant 10) (Variable "Hello")

Имена конструкторов типов

По умолчанию, имена конструкторов типов не могут состоять из символов набора ascSymbol. Однако можно принудительно разрешить использование таких имён для конструкторов типов. Это делается либо путём указания опции
-XTypeOperators при вызове ghc или ghci, либо путём добавления в начало hs-файла следующей строки:

{-# LANGUAGE TypeOperators #-}

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

Пример 6

{-# LANGUAGE TypeOperators #-}
data a @# b -- Конструктор типа
  -- Конструкторы данных:
  = XLeft a
   | XRight b
   | (a @# b) :$% (a @# b)
   | (a @# b) :!~ (a @# b)
   | (:!~>) (a @# b) (a @# b) (a @# b)
  deriving Show

Пробуем создать экземпляры нашего типа данных:

λ: let a = XLeft 10; b = XRight "ABCD"
λ: let c = a :$% b; d = b :!~ a;
λ: let e = (:!~>) a a a
λ: c
XLeft 10 :$% XRight "ABCD"
λ: d
XRight "ABCD" :!~ XLeft 10
λ: e
(:!~>) (XLeft 10) (XLeft 10) (XLeft 10)

Имена образцов

Имена образцов так же могут состоять из символов набора ascSymbol.

λ: let ($%%) = (*)
λ: :t ($%%)
($%%) :: Num a => a -> a -> a
λ: 5 $%% 2
10
λ: ($%%) 3 4
12
λ: let (###) = (3 +)
λ: (###) 2
5

Подводя итоги

Если речь идёт не об операторах, то вы вряд ли будете в именах использовать символы набора ascSymbol. Однако знание того, как они могут применяться за рамками имён операторов поможет вам понимать код, в котором они по каким-либо причинам всё же используются.

Автор: Hwd

Источник [2]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/haskell/80474

Ссылки в тексте:

[1] документации Haskell: https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-240003.2

[2] Источник: http://habrahabr.ru/post/248685/