Haskell vowel data type, is this possible?

I wrote the following code to remove vowels from a sentence:

main = print $ unixname "The House" vowel x = elem x "aeiouAEIOU" unixname :: [Char] -> [Char] unixname [] = [] unixname (x:xs) | vowel x = unixname xs | otherwise = x : unixname xs 

Just wondering if it's possible to create a data type for vowels? The compiler will not allow me to use characters in a data type.

+7
source share
2 answers

Not directly. The problem is that characters are a built-in type that does not have the ability to polymorphism. This is different from numeric literals that are designed for polymorphism using a class of type Num .

However, there are two main approaches you can take: a newtype shell with an intelligent constructor or a completely new type.

The newtype wrapper is easier to use:

 module Vowel (Vowel, vowel, fromVowel) where newtype Vowel = Vowel Char vowel :: Char -> Maybe (Vowel) vowel x | x `elem` "aeiouAEIOU" = Just (Vowel x) | otherwise = Nothing fromVowel :: Vowel -> Char fromVowel (Vowel x) = x 

Since the Vowel constructor is not exported, the new Vowel can only be created with the Vowel function, which only accepts the characters you want.

You can also create a new type:

 data Vowel = A | E | I | O | U | Aa | Ee | Ii | Oo | Uu fromChar :: Char -> Maybe Vowel fromChar 'a' = Just Aa fromChar 'A' = Just A -- etc. toChar :: Vowel -> Char toChar Aa = 'a' toChar A = 'A' 

This second method is rather difficult and therefore much more inconvenient to use.

So how to do it. I'm not quite sure what you want, though. The usual idiom is to create types that represent your data, and you specifically do not represent vowels. A typical template would look something like this:

 newtype CleanString = Cleaned { raw :: String } -- user input needs to be sanitized cleanString :: String -> CleanString 

Here, a new type distinguishes between unauthorized and disinfected entry. If the only way to make CleanString on CleanString , then you know statically that every CleanString is sanitized correctly (assuming CleanString correct). In your case, it seems that you really need a type for consonants, not vowels.

Haskell's Newtypes are very lightweight *, but the programmer has to write and use the code to wrap and deploy. In many cases, the benefits outweigh the extra work. However, I really can't come up with any application where it is important to know that your String is a vowel, so I probably just work with a simple String .

* newtypes exist only at compile time, so theoretically there is no need to use them at run time. However, their existence can change the generated code (for example, prohibit RULES), so sometimes a measurable impact on performance is observed.

+15
source

You can use phantom types to mark characters with extra information to ensure that the system system guarantees at compile time that your lines contain only, for example, vowels or unspoken ones.

Here is an example of a toy:

 {-# LANGUAGE EmptyDataDecls #-} import Data.Maybe newtype TaggedChar a = TaggedChar { fromTaggedChar :: Char } data Vowel data NonVowel isVowel x = x `elem` "aeiouyAEIOUY" toVowel :: Char -> Maybe (TaggedChar Vowel) toVowel x | isVowel x = Just $ TaggedChar x | otherwise = Nothing toNonVowel :: Char -> Maybe (TaggedChar NonVowel) toNonVowel x | isVowel x = Nothing | otherwise = Just $ TaggedChar x unixname :: [Char] -> [TaggedChar NonVowel] unixname = mapMaybe toNonVowel 

The advantage of this approach is that you can still write functions that work on all TaggedChars, regardless of the tag. For example:

 toString :: [TaggedChar a] -> String toString = map fromTaggedChar 
+8
source

All Articles