Search code examples
javascriptpattern-matchingffipurescript

Pattern matching in JS on a PureScript sum-of-products type


I have an algebraic sum-of-products type in PureScript:

data IO
    = Menu (Word8 -> Effect IO)
    | LongMessage (Array String) (Effect IO)
    | ShortMessage String (Effect IO)
    | End 

initialize :: Effect IO

I'd like to call initialize from JavaScript, and then consume its result. If I run Main.initialize() from JavaScript, I get a JS Object that prints like the following to console:

LongMessage3 {value0: Array(15), value1: ƒ}
value0: (1) ['foo']
value1: ƒ __do()
[[Prototype]]: Object

What would the JavaScript code look like that recognizes this object as a value corresponding to the LongMessage constructor?


Solution

  • First, to answer questions like this, you can just look at the compiled JS code yourself. It's located in ./output/My.Module.Name/index.js. Just open it and take a look.

    But for completeness, here's the answer:

    The current version of PureScript (0.15.2 as of this writing) compiles ADTs to a set of JS objects with unique prototypes.

    I'm going to use a smaller example, because yours is a bit too large to be legible. Let's say my ADT looks like this:

    data X = Y Int | Z String Boolean
    
    a = Y 42
    b = Z "foo" false
    

    This would yield roughly the following JS (I say "roughly" because it's a bit more elaborate, but the essence is the same):

    function Y(value0) {
      this.value0 = value0
    }
    
    function Z(value0, value1) {
      this.value0 = value0
      this.value1 = value1
    }
    
    var a = new Y(42)
    var b = new Z("foo", false)
    

    When constructed like this, values of the type can be tested for the particular constructor by checking the prototype via instanceof. So, for example, the following:

    c = case a of
      Y _ -> "It's a Y"
      Z s _ -> "It's a Z with " <> s
    

    would translate roughly to:

    var c = (() => {
      if (a instanceof Y) {
        return "It's a Y"
      }
    
      if (a instanceof Z) {
        return "It's a Z with " + a.value0
      }
    
      throw new Error("Invalid pattern match")
    })()
    

    A footnote: in your example the constructor seems to be named LogMessage3 instead of LogMessage. This tells me that you're probably executing a bundle. During bundling functions may get renamed to avoid naming conflicts - that's where the "3" comes from.