Haskell Language
Типированные отверстия
Поиск…
замечания
Одной из сильных сторон Haskell является способность использовать систему типов для моделирования частей вашего проблемного домена в системе типов. При этом часто встречаются очень сложные типы. При написании программ с этими типами (т. Е. Со значениями, имеющими эти типы), он иногда становится почти невосприимчивым к «жонглированию» всех типов. Начиная с GHC 7.8, появляется новая синтаксическая функция, называемая типизированными отверстиями. Типированные отверстия не изменяют семантику основного языка; они предназначены исключительно для помощи в написании программ.
Подробное объяснение типизированных отверстий, а также обсуждение конструкции типизированных отверстий см. В вики Haskell .
Раздел руководства пользователя GHC на типизированных отверстиях .
Синтаксис типизированных отверстий
Типовое отверстие является единственным символом подчеркивания ( _
) или допустимым идентификатором Haskell, который не находится в области видимости в контексте выражения. Перед существованием типизированных отверстий обе эти вещи вызовут ошибку, поэтому новый синтаксис не будет мешать никакому старому синтаксису.
Управление поведением типизированных отверстий
Поведение типизированных отверстий по умолчанию - это получение ошибки времени компиляции при столкновении с типизированным отверстием. Тем не менее, существует несколько флагов для точной настройки их поведения. Эти флаги суммируются следующим образом ( GHC trac ):
По умолчанию GHC имеет введенные отверстия и дает ошибку компиляции, когда он сталкивается с типизированным отверстием.
Когда
-fdefer-type-errors
или-fdefer-typed-holes
, ошибки отверстия преобразуются в предупреждения и приводят к ошибкам во время выполнения.По
-fwarn-typed-holes
флаг предупреждения-fwarn-typed-holes
. Без-fdefer-type-errors
или-fdefer-typed-holes
этот флаг является не-op, так как типизированные отверстия являются ошибкой в этих условиях. Если один из флажков отсрочки включен (преобразование введенных ошибок отверстий в предупреждения),-fno-warn-typed-holes
отключает предупреждения. Это означает, что компиляция полностью завершается успешно, и оценка отверстия приведет к ошибке выполнения.
Семантика типизированных отверстий
Значение отверстия типа может просто считаться undefined
, хотя типизированное отверстие вызывает ошибку времени компиляции, поэтому нет необходимости назначать ему значение. Однако типизированное отверстие (когда они включены) создает ошибку времени компиляции (или предупреждение с ошибками отложенного типа), в котором указывается имя типизированного отверстия, его выведенный наиболее общий тип и типы любых локальных привязок. Например:
Prelude> \x -> _var + length (drop 1 x)
<interactive>:19:7: Warning:
Found hole `_var' with type: Int
Relevant bindings include
x :: [a] (bound at <interactive>:19:2)
it :: [a] -> Int (bound at <interactive>:19:1)
In the first argument of `(+)', namely `_var'
In the expression: _var + length (drop 1 x)
In the expression: \ x -> _var + length (drop 1 x)
Обратите внимание, что в случае типизированных отверстий в выражениях, введенных в GHCi repl (как указано выше), также указывался тип введенного выражения, как it
(здесь тип [a] -> Int
).
Использование типизированных отверстий для определения экземпляра класса
Типизированные отверстия могут облегчить определение функций посредством интерактивного процесса.
Предположим, вы хотите определить экземпляр Foo Bar
класса (для вашего настраиваемого типа Bar
, чтобы использовать его с некоторой функцией полиморфной библиотеки, для которой требуется экземпляр Foo
). Теперь вы традиционно просматриваете документацию Foo
, выясняете, какие методы вам нужно определять, тщательно изучать их типы и т. Д. - но с типизированными отверстиями вы действительно можете пропустить это!
Сначала просто определите фиктивный экземпляр:
instance Foo Bar where
Компилятор теперь будет жаловаться
Bar.hs:13:10: Warning:
No explicit implementation for
‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’
Итак, нам нужно определить foom
для Bar
. Но что это такое? Опять же, мы слишком ленивы, чтобы посмотреть в документации и просто спросить компилятора:
instance Foo Bar where
foom = _
Здесь мы использовали типизированное отверстие как простой «запрос документации». Выходы компилятора
Bar.hs:14:10:
Found hole ‘_’ with type: Bar -> Gronk Bar
Relevant bindings include
foom :: Bar -> Gronk Bar (bound at Foo.hs:4:28)
In the expression: _
In an equation for ‘foom’: foom = _
In the instance declaration for ‘Foo Bar’
Обратите внимание , как компилятор уже заполнил переменный тип класса с конкретным типом Bar
, который мы хотим создать его экземпляр для. Это может сделать подпись намного понятнее, чем полиморфная, найденная в документации по классу, особенно если вы имеете дело с более сложным методом, например, с классом типа с несколькими параметрами.
Но что, черт возьми, Gronk
? На данный момент, вероятно, неплохо спросить Айоу . Однако мы все равно можем уйти без этого: в качестве слепой гипотезы мы предполагаем, что это не только конструктор типов, но и единственный конструктор значений, т. Gronk a
Он может использоваться как функция, которая каким-то образом приведет к Gronk a
. Поэтому мы пытаемся
instance Foo Bar where
foom bar = _ Gronk
Если нам повезет, Gronk
на самом деле является ценностью, и компилятор теперь скажет
Found hole ‘_’
with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
Where: ‘b0’ is an ambiguous type variable
Хорошо, это уродливо - сначала просто обратите внимание, что у Gronk
есть два аргумента, поэтому мы можем уточнить нашу попытку:
instance Foo Bar where
foom bar = Gronk _ _
И теперь это довольно ясно:
Found hole ‘_’ with type: [(Int, Bar)]
Relevant bindings include
bar :: Bar (bound at Bar.hs:14:29)
foom :: Bar -> Gronk Bar (bound at Foo.hs:15:24)
In the second argument of ‘Gronk’, namely ‘_’
In the expression: Gronk _ _
In an equation for ‘foom’: foom bar = Gronk _ _
Теперь вы можете продолжить прогресс, например, деконструируя значение bar
(компоненты будут отображаться вместе с типами в разделе « Relevant bindings
»). Часто в какой-то момент совершенно очевидно, что будет правильным определением, потому что вы видите все доступные аргументы и типы, подходящие друг другу, как головоломка. Или, альтернативно, вы можете видеть, что определение невозможно и почему.
Все это лучше всего работает в редакторе с интерактивной компиляцией, например Emacs с haskell-mode. Затем вы можете использовать типизированные отверстия так же, как запросы с загрузкой мыши в среде IDE для интерпретируемого динамического императивного языка, но без ограничений.