Выбор среди альтернатив в алгебраическом типе данных Haskell

StackOverflow https://stackoverflow.com/questions/1861432

Вопрос

Когда набираете X определяется как:

data X = 
    X { sVal :: String } |
    I { iVal :: Int } |
    B { bVal :: Bool }

и я хочу, чтобы Int внутри X значение, если оно есть, в противном случае равно нулю.

returnInt :: X -> Int

Как я могу определить, какой тип X аргумент к returnInt есть?

Это было полезно?

Решение

Просто чтобы прояснить здесь один момент, позвольте мне переписать ваш тип данных, чтобы избежать двусмысленностей в значении X:

data SomeType = X { myString :: String} | I {myInt :: Int} | B {myBool :: Bool}

В этом определении нет типов X, I и B.X, I и B являются конструкторы , создающие значение типа Sometype .Обратите внимание, что происходит, когда вы спрашиваете ghci, каков тип любого значения, созданного с помощью этих конструкторов типов:

*Main> :t (I 5)
(I 5) :: Sometype 

*Main> :t (B False)
(B False) :: Sometype

Они принадлежат к одному и тому же типу!!

Точно так же, как вы можете использовать X, I и B для построения типов, вы можете использовать сопоставление с шаблоном для деконструкции типа, как это сделано в других ответах выше:

returnInt :: SomeType -> Int 
returnInt (I x) = x        -- if the pattern matches (I x) then return x
returnInt _  = error "I need an integer value, you moron"  -- throw an error otherwise

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

Обратите внимание, что когда вы определяете свой тип, как вы это делали, используя то, что называется синтаксисом записи (просто посмотрите здесь: http://en.wikibooks.org/wiki/Haskell/More_on_datatypes ), вы получаете подобные функции бесплатно!!

Попробуйте посмотреть на тип myInt, например:

*Main> :t myInt
myInt :: SomeType -> Int

И посмотрите, что делает эта функция:

*Main> myInt (I 5)
5

*Main> myInt (B False)
*** Exception: No match in record selector Main.myInt

Это именно то поведение, которое returnInt выше определенный.Странное сообщение об ошибке просто сообщает вам, что функция не знает, как обращаться с элементом типа SomeType, который не соответствует (I x).

Если вы определяете свой тип, используя более распространенный синтаксис:

data SomeType2 = X String | I Int | B Bool

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

Сообщения об ошибках завершают выполнение программы.Иногда это раздражает.Если вам нужно более безопасное поведение для ваших функций, ответ GBacon - это просто способ сделать это.Узнайте о Maybe a введите и используйте его, чтобы справиться с такого рода вычислениями, которые должны возвращать некоторое значение или ничего не возвращать ( попробуйте это: http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Maybe ).

Другие советы

Используйте сопоставление с образцом.

returnInt :: X -> Int
returnInt (I x) = x
returnInt _     = 0

Используйте более гибкое определение для всех возможных X ценности:

returnInt :: X -> Maybe Int
returnInt (I i) = Just i
returnInt _ = Nothing

Тогда вы можете использовать maybe для конкретного значения по умолчанию, которое вы хотите,—0 может быть допустимым значением (это известно как полупредикатная проблема):

*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> maybe (-1) id (returnInt $ X "yo")
-1

Напротив, частичные функции рискуют вызвать исключения во время выполнения:

*Main> let returnInt (I i) = i
*Main> :t returnInt
returnInt :: X -> Int
*Main> returnInt (B True)
*** Exception: <interactive>:1:4-22: Non-exhaustive patterns in function returnInt

Если вы чувствуете себя по-настоящему лягушачьим, вы могли бы использовать MonadPlus

returnInt :: (MonadPlus m) => X -> m Int
returnInt (I i) = return i
returnInt _ = mzero

чтобы получить еще большую гибкость:

*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> returnInt (I 123) `mplus` returnInt (I 456) :: [Int]
[123,456]

Учитывая функцию, подобную этой:

returnInt :: X -> Int
returnInt x = {- some integer -}

...тип x всегда есть X.О чем вы заботитесь, так это о том, будет ли x использует X, I или B введите конструктор.

Используйте сопоставление с образцом, чтобы определить разницу:

returnInt :: X -> Int
returnInt (X _) = error "needed an Int, got a String"
returnInt (I { iVal = n }) = n
returnInt (B _) = error "needed an Int, got a Bool"
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top