2

I am working on a homework assignment and we have an array of mixed elements Char and Int.

I created this custom type:

data Element = CharToElement Char | IntToElement Int 

Which converts Chars and Ints into Elements so I can deal with them.

The issue I am having is what to do when I want to convert back?

So now that I do (head array) and that returns Element how do I convert that Element to an Int or Char.

This is for a postfix evaluator program.

Help is appreciated. Thanks.

Edit:

So I worked on it and this is what I have:

data Element = ElementConstructor (Char, Int, Bool) | IntToElement Int | CharToElement Char | NIL extractInt (_,i,_) = i extractChar (c,_,_) = c extractType (_,_,b) = b stack_push :: [Element] -> Element -> [Element] stack_push stack element = (element:stack) stack_pop :: [Element] -> [Element] stack_pop stack = (tail stack) stack_top :: [Element] -> Element stack_top stack = (head stack) postfix_eval eqn = (postfix_eval_rec (postfix_eval_const eqn []) []) postfix_eval_const eqn_raw eqn_conv = if null eqn_raw then NIL else if (isNumber (head eqn_raw)) then ElementConstructor(NIL, (head eqn_raw), True):eqn_conv else if (isAlpha (head eqn_raw)) then ElementConstructor((head eqn_raw), NIL, False):eqn_conv postfix_eval_const (tail eqn_raw) eqn_conv operate :: Element -> Element -> Element -> Element operate operator_char a b = if operator_char == '+' then IntToElement ( (extractInt a) + (extractInt b) ) else if operator_char == '-' then IntToElement ( (extractInt b) - (extractInt a) ) else if operator_char == '*' then IntToElement ( (extractInt a) * (extractInt b) ) else if operator_char == '/' then IntToElement ( (extractInt b) `div` (extractInt a) ) else IntToElement(0) postfix_eval_rec :: [Element] -> [Element] -> Int postfix_eval_rec eqn stack = if null eqn then (extractInt (stack_top stack)) else if ( (extractType (head eqn)) ) then (postfix_eval_rec (tail eqn) (stack_push stack (head eqn) ) ) else (postfix_eval_rec (tail stack) (stack_push (stack_pop (stack_pop stack)) (operate (head eqn) (stack_top stack) (stack_top (stack_pop stack))))) 

The idea is that you enter something like: postfix_eval [1,2,3,'+',4,'+','*'] and you get an answer in the form of an Int, in this case you get 9

10
  • Possible duplicate of Haskell - Return item from custom type Commented Oct 26, 2016 at 0:22
  • 4
    (The keywords you are looking for are "pattern matching" and "case expression".) Commented Oct 26, 2016 at 0:27
  • I really don't get this concept. I looked at the possible duplicate but the person on that question is using a tuple and mashing everything together. That is not my case. Can you explain using what I gave you above in code how I would do it? Commented Oct 26, 2016 at 0:30
  • 2
    No, it isn't. Please search for the keywords I suggested. To get you started, here is a better duplicate, with plenty of examples in the answers: Haskell type and pattern matching question: extracting fields from a data type. Commented Oct 26, 2016 at 1:17
  • 1
    What representation will you use for your "either Int or Char"? (or in other words, you want to write a function getIntOrChar :: Element -> ???) Commented Oct 26, 2016 at 3:13

1 Answer 1

4

There are many ways to fix this. I'll only provide some general suggestions.

First of all, strive to use only total functions -- functions that never error out. In particular, head, tail are unnecessary in most idiomatic Haskell code, and should be regarded as a code smell.

Further, avoid if/guards that can be replaced by pattern matching. You really should get used to that, and there are good tutorials around.

For instance, here's a first reworking of your operate function:

operate :: Element -> Element -> Element -> Element operate (CharToElement '+') a b = IntToElement (extractInt a + extractInt b) operate (CharToElement '-') a b = IntToElement (extractInt a - extractInt b) operate (CharToElement '*') a b = IntToElement (extractInt a * extractInt b) operate (CharToElement '/') a b = IntToElement (extractInt a `div` extractInt b) operate _ _ _ = IntToElement 0 

This is far from being perfect. First, why returning 0 on error? There's no way the caller can distinguish that from an actual null result. Second, the function extractInt will necessarily be partial (or again return a bogus result): what if a is a char instead?

Ultimately, the big lie is already found at the type level:

operate :: Element -> Element -> Element -> Element 

This type indeed lies: we do not always have a resulting Element for all inputs. We want to model errors instead. This is more honest:

operate :: Element -> Element -> Element -> Maybe Element 

and can be implemented, again, by pattern matching:

operate :: Element -> Element -> Element -> Maybe Element operate (CharToElement '+') (IntToElement a) (IntToElement b) = Just $ IntToElement $ a + b operate (CharToElement '-') (IntToElement a) (IntToElement b) = Just $ IntToElement $ a - b operate (CharToElement '*') (IntToElement a) (IntToElement b) = Just $ IntToElement $ a * b operate (CharToElement '/') (IntToElement a) (IntToElement b) = Just $ IntToElement $ a `div` b operate _ _ _ = Nothing 

And voila, no need for dangerous extract functions. When the inputs are not integers, we simply return Nothing since the first matches fail.

This might feel less convenient, since now we need to handle the Maybe Element in the rest of the code, which no longer receives a simple Element to work with. But this is the whole point! The rest of the code needs to cope with the possible errors, so the error handling must be performed there as well. The Maybe Element type forces us to do that.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.