Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have a "case ... of" statement on a tag of a custom type?

Tags:

I would like to write a function that encodes a custom type Decision a into JSON. The a tag could be e.g. Accept or Reject, and I would like my function to call other functions depending on the type of the tag.

I have tried writing the case ... of statement both in terms of the Decision type as well as the tag, but have not been able to get either approach to work.

Here are my types:

type Decision a
    = Decision Metadata a


type Accepted
    = Accepted AcceptDetails


type Rejected
    = Rejected RejectDetails


type alias Metadata =
    { time : Time.Posix }


type alias AcceptDetails =
    { comment : String }


type alias RejectDetails =
    { reasonCode : Int }

Now I would like to write an encoder that can take either type of decision. Something along the lines of:

encoder : Decision a -> Json.Encode.Value
encoder decision =
    case decision of
        Decision _ (Accepted _) ->
            acceptedEncoder decision

        Decision _ (Rejected _) ->
            rejectedEncoder decision

This does not work but hopefully it conveys what I want - to route the encoding task to the appropriate function.

Is this possible? Or do I have to solve it another way, e.g. by directly calling the right encoder depending on the kind of decision?

I suppose there is a catch in the fact that a could be anything, but, perhaps naively, that could be handled by adding a _ case as well.

The compiler gives the error message:

The first pattern is trying to match `Decision` values of type:

    Decision Accepted

But the expression between `case` and `of` is:

    Decision a
like image 456
Erik Eng Avatar asked Jul 24 '19 09:07

Erik Eng


1 Answers

If the a in Decision a is only ever going to be either an Approved or Rejected type, it seems that merging the two constructors into a single type would be appropriate.

type DecisionResult
    = Accepted AcceptDetails
    | Rejected RejectDetails

This would remove the need for the type parameter on Decision:

type Decision
    = Decision Metadata DecisionResult

Your encoder function could then route the decision metadata and approved/rejected details to the more specific encoders:

encoder : Decision -> Json.Encode.Value
encoder (Decision meta result) =
    case result of
        Accepted details ->
            acceptedEncoder meta details

        Rejected details ->
            rejectedEncoder meta details

acceptedEncoder : Metadata -> AcceptDetails -> Json.Encode.Value
acceptedEncoder meta details =
    ...

rejectedEncoder : Metadata -> RejectDetails -> Json.Encode.Value
rejectedEncoder meta details =
    ...
like image 191
Chad Gilbert Avatar answered Oct 19 '22 02:10

Chad Gilbert