2013-11-19 3 views
2

Обычно, когда я создаю функции я не знаю, в каких функциях лучше использовать классы типов (Eq, Integral и т.д.), потому что иногда нет необходимости использовать их любят:Когда использовать модельные стекла?

factorial :: Int -> Int 
bla bla bla 
bla bla bla 

и

factorial :: (Integral a) => a -> a 
bla bla bla 
bla bla bla 

Я считаю, что второй один только что нашел время и место но в функции Эля, важно, чтобы написать Eq (ниже elleme является elem)

elemme :: Eq a => a -> [a] -> Bool 
y `elemme` [] = False 
y `elemme` (x:xs) = if y == x then True else y `elemme` xs 

Пожалуйста, сообщите мне об этом. Благодарю.

+1

Это может вам помочь: http://stackoverflow.com/questions/17100036/should-i-use-typeclasses-or-not?rq=1 – Sibi

+1

Подумайте о свойствах функции/args. Какие свойства вы хотите использовать в своей функции/args ?. Во-первых, поймите о свойствах ('Eq',' Ord', 'Monoid', ...), тогда вы можете ограничить свои функции работой * с аргументами (свойствами соответствия) * (вместо явных аргументов, таких как' Int', ' Bool', ...). – josejuan

+6

'factorial' на самом деле плохой пример, потому что если вы используете' Int' вместо 'Integer', он дает неправильный ответ для любого числа выше 20. –

ответ

4

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

Вы идете: «Но что, если это значение является функцией?» Или «Что, если я разрешу это для любого функтора, что будет результатом для других функторов? Полезно ли это?» Как оказалось, это во многих случаях! И это одна из причин, по которой Хаскелл настолько велик, на мой взгляд. Легко «случайно» создавать очень многократно используемые функции.

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


@jozefg сделал отличную оценку общности, ограничивающей возможные реализации. Я просто хочу дать больше внимания, потому что это на самом деле очень мощная концепция. С помощью нескольких общих функций вы можете быть абсолютно уверены, что функция работает только на основе сигнатуры типа, потому что для этой общей подписи существует только одна возможная реализация.

Недавно я столкнулся подпись

mystery :: (a -> b -> c) -> (a -> b) -> a -> c 

, который интересен тем, что мы можем на самом деле выяснить, какую функцию она основана на том, что он делает. У нас есть

mystery :: (a -> b -> c) -> (a -> b) -> a -> c 
mystery f    g   x = ... 

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

Решение, конечно, применять g x получить значение b, который мы затем можем придерживаться в f, таким образом, в конце концов возвращает значение c. Описание это немного сложно следовать, но если вы работаете с ним в вашей голове вы прибудете на

mystery f g x = f x (g x) 

, который выполняет то же самое, что и функция библиотеки ap. (От Control.Applicative.)

Это очень круто, что вы можете понять, что функция делает исключительно на основе ее подписи!

9

Typeclasses позволяют писать более общие функции. Haskell имеет большие инструменты для позволяя вам определить, что функция работает над всеми типами, как

id :: a -> a 
id a = a 

Но некоторые вещи не работают над всеми типами,

(==) :: Eq a => a -> a -> Bool 

Без классов типов, мы должны либо написать новый == для каждого типа,

eqInteger :: Integer -> Integer -> Bool 

Или указать, что все типы equatable и могут быть проверены на равенство. Но тогда вы можете спросить

id == const 1 

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

Я часто предпочитаю писать эти более общие функции, даже если я не использую их более чем с одним типом, поскольку чем более общая подпись, тем меньше вариантов для реализации, что позволяет легче узнать, что я «т сделать что-то глупое, как

id :: Integer -> Integer 
id = (+1) 

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

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