2015-08-17 6 views
22

Благодаря some excellent answers here, я вообще понимаю (явно в ограниченной форме) с целью Хаскеля Maybe и что его определениеПочему возможно включить Just?

data Maybe a = Nothing | Just a 

однако я не сущность ясно, почему именно Just является частью этого определения. Насколько я могу судить, именно здесь определяется Just, но в соответствующей документации об этом мало говорится.

Я правильно думаю, что основное преимущество использования Just в определении Maybe, а не просто

data Maybe a = Nothing | a 

является то, что она позволяет шаблон, чтобы с Just _ и полезными функциями, как isJust и fromJust ?

Почему Maybe определяется как прежний, а не последний?

ответ

33

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

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

data Maybe a = Nothing | a 

Там нет "метки" для a здесь. Как мы скажем Maybe a, кроме обычного, развернутого a в вашем случае?

Maybe имеет Just конструктор, поскольку он имеет иметь конструктор дизайн.

На других языках есть union types, которые могут работать так, как вы думаете, но они не подходят для Haskell. Они играют по-разному на практике и склонны быть склонными к ошибкам.

Есть несколько серьезных причин для предположения о том, что тегированные союзы предпочитают обычные типы соединений. Они хорошо играют с типом вывода. Союзы в реальном коде часто имеют метку в любом случае¹. И, с точки зрения элегантности, меченые союзы - это естественная подгонка на язык, потому что они являются двойными от продуктами (т.е. кортежей и записей). Если вам интересно, я написал об этом в блоге introducing and motivating algebraic data types.

сносок

¹ Я играл с типами профсоюзов в двух местах: машинопись и C. машинопись компилирует JavaScript, который динамически типизированных, означает, что он отслеживает тип значения во время выполнения, в основном, тег.

C не только на практике, то что-то вроде 90% использования типов профсоюзов либо имеет тег, либо эффективно эмулирует структурный подтипирование. Один из моих профессоров на самом деле сделал эмпирическое исследование о том, как профсоюзы используются в реальном коде С, но я не помню, какая бумага была в стороне.

+0

Значит, это своего рода (преднамеренный) непредвиденный, в некотором смысле? Язык требует конструктора, и один из них позволяет совпадать с шаблоном 'Just _' и' isJust' и т. Д. – orome

+3

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

+0

Да, немного дерева, трясущегося от дизайнеров. – orome

7

Just является конструктором, a в одиночку будет иметь тип a, когда Just a строит другой тип Maybe a.

+0

Ах, конечно. Я этого не видел (я ясно знаком с Хаскеллом). И (как в моем комментарии ниже) тот факт, что он позволяет некоторое дополнительное удобство, действительно является побочным эффектом. – orome

17

Другой способ смотреть на него (в дополнение к ответу Тихона), чтобы рассмотреть еще один из основных типов Haskell, Either, которая определяется следующим образом:

-- | A value that contains either an @[email protected] (the 'Left') constructor) or 
-- a @[email protected] (the 'Right' constructor). 
data Either a b = Left a | Right b 

Это позволяет иметь значения, как это:

example1, example2 :: Either String Int 
example1 = Left "Hello!" 
example2 = Right 42 

... но как это одна:

example3, example4 :: Either String String 
example3 = Left "Hello!" 
example4 = Right "Hello!" 

Тип Either String String, в первый раз, когда вы сталкиваетесь с этим, звучит как «либо String, либо String», и поэтому вы можете подумать, что это то же самое, что и String. Но это не так, потому что союзы Haskell составляют с тегами союзов, и поэтому Either String String записывает не только String, но также и те из «тегов» (конструкторы данных, в данном случае Left и Right). Поэтому, хотя обе альтернативы несут String в качестве своей полезной нагрузки, вы можете сказать, как было построено какое-то одно значение. Это хорошо, потому что есть много случаев, когда альтернативы имеют тот же тип, но конструкторы/теги придают дополнительный смысл:

data ResultMessage = FailureMessage String | SuccessMessage String 

Здесь Конструкторы данные являются FailureMessage и SuccessMessage, и вы можете догадаться из названия, что даже хотя полезная нагрузка в обоих случаях равна String, они означают совсем другие вещи!

Так вернуть его к Maybe/Just, что тут происходит, что Haskell просто равномерно работает так: каждая альтернатива типа союза имеет ярко выраженный конструктор данных, которые всегда должны быть использованы для построения и значения сопоставления с образцом его типа , Даже если сначала вы могли бы подумать, что можно было бы угадать это из контекста, это просто не делает этого.

Есть и другие причины, немного более технические. Во-первых, правила для ленивой оценки определяются с точки зрения конструкторов данных. Короткий вариант: ленивая оценка означает, что если Haskell вынужден заглянуть внутрь значения Maybe a, он попытается выполнить минимальный объем работы, необходимый для выяснения, похоже ли это на Nothing или как Just x - возможно, t заглянуть внутрь x когда он делает это.

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

data Maybe a = Nothing | a -- NOT VALID HASKELL 

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

example5, example6 :: Maybe (Maybe a) 
example5 = Nothing 
example6 = Just Nothing 

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

-- Map of persons to their favorite number. If we know that some person 
-- doesn't have a favorite number, we store `Nothing` as the value for 
-- that person. 
favoriteNumber :: Map Person (Maybe Int) 

... и хочу посмотреть запись:

Map.lookup :: Ord k => Map k v -> k -> Maybe v 

Так что, если мы посмотрим на mary на карте мы имеем:

Map.lookup favoriteNumber mary :: Maybe (Maybe Int) 

И теперь результат Nothing означает Мэри не на карте, в то время как Just Nothing означает Марии на карте, но она не имеет любимый номер.

3

Maybe a сконструирован таким образом, чтобы иметь еще одно значение, чем тип a. В теории типов иногда написано как 1 + a (до iso), что делает этот факт еще более очевидным.

В качестве эксперимента рассмотрите тип Maybe (Maybe Bool). Здесь мы имеем 1 + 1 + 2 значения, а именно:

Nothing 
Just Nothing 
Just (Just False) 
Just (Just True) 

Если бы мы позволили определить

data Maybe a = Nothing | a 

мы не потеряли бы различие между случаями Just Nothing и Nothing, так как больше не Just сделать их друг от друга , Действительно, Maybe (Maybe a) рухнет на Maybe a. Это было бы неудобным частным случаем.

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