Traits type class
From HaskellWiki
| (3 intermediate revisions not shown.) | |||
| Line 37: | Line 37: | ||
<code> | <code> | ||
HaskellPrompt> mantissaDigits (undefined :: Float) | HaskellPrompt> mantissaDigits (undefined :: Float) | ||
| + | |||
24 | 24 | ||
</code> | </code> | ||
| - | This technique works well in conjunction with [[Functional | + | This technique works well in conjunction with [[Functional dependencies]]. For example, there may be some float types for which an Int may not be sufficient to express the number of digits in the mantissa: |
<haskell> | <haskell> | ||
| Line 53: | Line 54: | ||
</haskell> | </haskell> | ||
| - | You can also use this technique as an alternative to [[Higher order function]] in cases where you need several functions which work together. | + | You can also use this technique as an alternative to [[Higher order function]]s in cases where you need several functions which work together. |
Consider, for example, converting strings from upper case to lower case and back again. This in general depends on the language that you are operating in. The lower case version of 'A', for example, is different in English than in Greek. You can wrap this up in a typeclass parameterised on language: | Consider, for example, converting strings from upper case to lower case and back again. This in general depends on the language that you are operating in. The lower case version of 'A', for example, is different in English than in Greek. You can wrap this up in a typeclass parameterised on language: | ||
| Line 75: | Line 76: | ||
</haskell> | </haskell> | ||
| - | This way, you only need pass around one "language" parameter rather than two [[Higher order function]]. | + | This way, you only need pass around one "language" parameter rather than two [[Higher order function]]s. |
[[User:AndrewBromage]] | [[User:AndrewBromage]] | ||
| + | |||
| + | A very nice example by Lennart is found in his [http://augustss.blogspot.com/2007/04/overloading-haskell-numbers-part-3.html blog] | ||
[[Category:Idioms]] | [[Category:Idioms]] | ||
Current revision
Occasionally you want to associate information with a type, not just a value. An example is the standardclass Bounded a where minBound :: a maxBound :: a
However, this technique does not work if the information which you want to extract doesn't have the type in its signature. One example is floating point format information, such as:
class FloatTraits a where -- This one is fine epsilon :: a -- This one is not mantissaDigits :: Int -- etc
The solution is to pass a Reified type as a phantom argument:
class FloatTraits a where mantissaDigits :: a -> Int instance FloatTraits Float where mantissaDigits _ = 24 instance FloatTraits Double where mantissaDigits _ = 53
HaskellPrompt> mantissaDigits (undefined :: Float)
24
This technique works well in conjunction with Functional dependencies. For example, there may be some float types for which an Int may not be sufficient to express the number of digits in the mantissa:
class (Integral i) => FloatTraits a i | a -> i where mantissaDigits :: a -> i instance FloatTraits Float Int where mantissaDigits _ = 24 instance FloatTraits ArbitraryPrecisionFloat Integer where mantissaDigits x = {- detail omitted -}
You can also use this technique as an alternative to Higher order functions in cases where you need several functions which work together.
Consider, for example, converting strings from upper case to lower case and back again. This in general depends on the language that you are operating in. The lower case version of 'A', for example, is different in English than in Greek. You can wrap this up in a typeclass parameterised on language:
class CaseConvert language where toUpperCase :: language -> String -> String toLowerCase :: language -> String -> String data EnglishLanguage = EnglishLanguage instance CaseConvert EnglishLanguage where toUpperCase _ s = {- etc -} toLowerCase _ s = {- etc -} data GreekLanguage = GreekLanguage instance CaseConvert GreekLanguage where toUpperCase _ s = {- etc -} toLowerCase _ s = {- etc -}
This way, you only need pass around one "language" parameter rather than two Higher order functions.
A very nice example by Lennart is found in his blog
