简体   繁体   中英

Deriving length function from List in data class

I have a data class that consists of two aliases: String and [String] , ie [Char] and [[Char]] . Currently it derives Eq and Show :

data BashVar = BashString String | BashArray [String] deriving (Eq,Show)

Why can't it derive also from [_] or however the generic list type is called?

I just want to be able to use list functions on instances of the class, specifically length .

There is no class for lists. length is defined simply as:

length :: [a] -> Int

Not (as I think you imagine):

class ListLike l where
  length :: l a -> Int

The closest class to list is probably Foldable : http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Foldable.html But that doesn't define length , just various folds.

Besides which, if there was a list class, it would probably still allow the inner type to vary, as in my example above. Your BashVar class doesn't allow any type in the lists, it's fixed to lists of String. So even if ListLike existed, you couldn't support it (and you can't derive Foldable for the same reason).

Both Eq and Show are typeclasses , while [] is a data type . If you look at the type of (==) , it's Eq a => a -> a -> Bool , while length is [a] -> Int . The difference is that the first one has a typeclass constraint Eq a , and the second one doesn't depend on a typeclass constraint at all, merely on the data type [a] . Unfortunately, there's no way to make the default length work with custom data types.

Your best bet is to defined your own functions:

bashLength :: BashVar -> Int
bashLength (BashString s) = length s
bashLength (BashArray xs) = length xs

From your question and comments, where you say a nonsense like the following:

  • I have a data class
  • Why can't it derive also from [_] or however the generic list type is called?
  • BashVar is an instance of [a]

it's easy to detect that you're trying to "jump" the language without spending the least amount of time at reading even the most basic tutorials.

Getting yourself acquainted at least with the "Types and Typeclasses" chapter of "Learn You a Haskell" should erase all the questions you've asked here. It would be merely impossible to explain things otherwise, because the language and most of its concepts are way too different from any OOP language you may be trying to project on it.

So your datatype can be either an array of strings or a single string? What do you want your length function to return? If you do

bashLength :: BashVar -> Int
bashLength (BashString s) = length s
bashLength (BashArray xs) = length xs

you get either the length of the string:

Main> bashLength (BashString "hello")
5

or the length of the array:

Main> bashLength (BashArray ["1","2","3"])
3

So both cases are in fact very different in meaning. Is that what you want? It is not a very list-like behaviour. Look at the implementation of the list datatype and the length function, if you want to dive more into this: http://www.haskell.org/onlinereport/standard-prelude.html

If you just want your type to behave exactly like a list of strings, you could use a type synonym and get all the functions of the list type for free:

type BashVar = [String]

Or you could try a recursive datatype, in the same way the list type is defined, to mimick the list behaviour:

data BashVar = BashString String | BashArray [BashVar] deriving (Show)

Now, I would make bashLength just return the length of the array - but I'm not sure if that's what you want to express:

bashLength :: Num a => BashVar -> a
bashLength (BashString s) = 1
bashLength (BashArray xs) = sum (map bashLength xs)

Let's try it:

Main> bashLength(BashArray [])
0
Main> bashLength(BashArray [BashString "b",BashString "c"])
2

Still, if you use this approach, you have to name your function "bashLength" or something like that, because "length" is already defined on lists, and overloading of functions in Haskell works via type classes. The list type, at least in in Haskell 98, is a datatype, not a type class whose functions you could implement in your own datataype "BashVar". To circumvent this limitation, there are even more tricks than wrapping the list type in your own datatype, in case you want to read more: http://www.haskell.org/haskellwiki/List_instance

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM