Turning a map of data constructors into a row type

Over the past two weeks my team and I have been able to use a row of types to generate all kinds of code to run well-typed forms in Halogen components with minimal boilerplate. It’s been working wonderfully, but now we have to turn to a new challenge: dynamic forms.

I’d like to use this same approach with dynamic forms, but there’s a hitch: we have to serialize and deserialize our form configurations, so we just store a data constructor representing what the type of the field ought to be instead of the literal PureScript type.

To briefly summarize my problem: I have a map of ids to data constructors like this:

data InputType
  = TextField
  | NumberField

myInputs :: Map Int InputType
myInputs = Map.fromFoldable [ Tuple 0 TextField, Tuple 1 NumberField ]

I want to write this function:

makeRow :: Map Int InputType -> ( ref0 :: String, ref1 :: Number )
makeRow = ?a

As to why, it’s because we’ve got all kinds of lovely code that can go from a row like that and generate all the various types and initial states and so on for forms running in Halogen components. And the reason I can’t just write the row in the first place is because we’re getting this information via JSON, it’s not in PureScript.

Previous discussions about generating code can be found here:

Is it possible to write this function? If so, I’d love to hear advice, and if not, I’d really love to hear advice.

1 Like

You wouldn’t be able to go straight to a row like that, as rows don’t have corresponding values, they only exist at the type level. If you do only purely want a row and not something like a Variant result you could return a proxy though.

It looks like what you’re wanting here would require dependant types - assuming you want to calculate the return value of makeRow purely from the input map, rather than it being static like this example? That would only be possible in PS if the information about the map contents still existed in some way that it could be used at compile time.

You would probably be better off doing this the other way around: specify the row that you want, and generating the map from it instead. :smiley:

1 Like

That’s right, I was trying to produce a row purely from the input map, probably with reifySymbol. But given that I don’t even have the map at compile time (it’s decoded from JSON), I realize I can’t have compile-time proof of anything I’d need to construct the row proxy or even a record.

It looks like I’ll need to stick with the map, and to get my various types I’ll need a sum type wrapper like this:

data Field
  = Text String
  | Num Number
  | Radio RadioTypes

data RadioTypes
  = Ids AccountId
  | etc

Perhaps this could be made nicer, but it seems like I’ll have to do this for every kind of input we’ll need in our dynamic forms, and that’s pretty clunky.