简体   繁体   中英

Trying to construct a list of a custom datatype in Haskell

I am pretty new to Haskell and I am trying to create a list of a custom data type called Human . Human is defined like this: data Human = Human(String), so a Human is essentially a String. A human is defined by whether they are tall or short (S or T), female or male (F or M), and if they are an adult or child (A or C).

I have tried writing a function which basically gives me a list of persons of every possible combination of the values above, ie = ["SFA", "SFC, "TMC"...]. This is what I have come up with this far:

func :: ([Human], state)
func = (x, state_) where
remainingHumans = [[height, sex, age] | height <- ["T", "S"], sex <- ["M", "F"], age <- ["A", "C"]]
allHumans = [Person(human) | human <- subsequences remainingHumans, length human == 3]
x = head allHumans
state _ = allHumans \\ [x]

I am getting this error when I try to compile the program:

Couldn't match type ‘[[Char]]’ with ‘Char’
  Expected type: String
    Actual type: [[[Char]]]

As I am pretty new I am also pretty lost on how to proceed. Any help or hints would be greatly appriciated

I think since height , sex and age are essentially characters , it is probably better to present these as characters. So we can write height <- ['T', 'S'] instead of height <- ["T", "S"] , or even shorter height <- "TS" . If we construct a list of String s, we do not construct a String , but - well - a list of String s.

Since you have access to the characters, you can construct a list of the three characters height , sex and age , by constructing a list : a String is just a type alias for [Char] .

We can also make other improvements. Since we construct a list of three elements, the length is always three, so we do not need to check this later in the process, and we can make Human s (I assume you mixed up Person and Human ) directly.

You specify state , in the signature, but that is a type variable. It would mean that it can be anything . Here however it is clearly a list of Human s, so you need to change the sgianture to (Human, [Human]) .

Finally we do not have to construct a head to obtain the first element, but can use pattern matching, nor do we have to remove that element from the list: since all elements are unique, the remaining elements are all in the tail (and no other element is in the tail):

func :: ([Human], [Human])
func = (x, state_) where
    (x:state_) = [Human [height, sex, age] | height <- "TS", sex <- "MF", age <- "AC"]

Possible improvements

I think the modeling might be better if you define a Human as:

data Human = Human Height Sex Age
data Height = Tall | Small
data Sex = Male | Female
data Age = Adult | Child

By doing this, you limit the number of humans that can be constructed by design . Indeed, right now it might be possible that a function contains a small error, and produces Human "ABC" (characters not in the "domain"), or Human "TM" (too few characters), or Human "AMT" (swapped order). This can result in a lot of trouble later in the process. By specifying the domains, etc. in the type, functions simply can not generate such values (well the compiler will raise an error). So it gives you "stronger guarantees" that the function is working properly. The function, of course, can still contain a semantical error.

Furthermore by defining separate parameters, you also enforce that every human has these three attributes, and the constructor is now curried .

It would also make the process of creating humans easier with the (<$>) :: f (a -> b) -> fa -> fb and (<*>) :: f (a -> b) -> fa -> fb functions:

Human <$> [Tall, Small] <*> [Male, Female] <*> [Adult, Child]

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