Search code examples
jsonhaskellhaskell-snap-framework

Haskell Snap rendering JSON from route


I'm new to Snap and Haskell. I am attempting to turn this output:

[("LDPNLNDQQFHUKIWZII",2191.12),("NLNDQQFHUKIWZIIXDC",2195.0696),("NZRAIEUALDPNLNDQQFH",2192.0503)]

into JSON. So I know the type is [(String, Double)], but that doesn't help me with the error message arising from GHCi when entering writeJSON [("LDPNLNDQQFHUKIWZII",2191.12),("NLNDQQFHUKIWZIIXDC",2195.0696),("NZRAIEUALDPNLNDQQFH",2192.0503)]:

No instance for (MonadSnap m0) arising from a use of `writeJSON' The type variable `m0' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there are several potential instances: instance MonadSnap m => MonadSnap (Control.Monad.Trans.Cont.ContT c m) -- Defined in `snap-core-0.9.5.0:Snap.Internal.Instances' instance (MonadSnap m, Control.Monad.Trans.Error.Error e) => MonadSnap (Control.Monad.Trans.Error.ErrorT e m) -- Defined in `snap-core-0.9.5.0:Snap.Internal.Instances' instance MonadSnap m => MonadSnap (Control.Monad.Trans.List.ListT m) -- Defined in `snap-core-0.9.5.0:Snap.Internal.Instances' ...plus 8 others In the expression: writeJSON x In an equation for `it': it = writeJSON x

I'm not married to the current data structure (I just created it to clump an amino acid sequence with its mass). Ideally I would get JSON data that looks like this:

{ "LDPNLNDQQFHUKIWZII":2191.12, "NLNDQQFHUKIWZIIXDC":2195.0696, "NZRAIEUALDPNLNDQQFH":2192.0503 }

The output, [("LDPNLNDQQFHUKIWZII",2191.12),("NLNDQQFHUKIWZIIXDC",2195.0696),("NZRAIEUALDPNLNDQQFH",2192.0503)], is calculated by a function that takes two pieces of input from the user: an original peptide sequence, and a weight (the program finds fragments from the peptide sequence that equal the entered weight).

Any help/direction is appreciated. Thanks!

ADDITIONAL INFORMATION:

I generated the app using snap init barebones. Here is what the handler looks like. I am getting the error at the writeJSON result line. I am intending on using the getParam lines, but to start I was just trying to get the JSON response.

possibleMatches :: Snap ()
possibleMatches = do
  peptideSequence <- getParam "peptide_sequence"
  weight          <- getParam "weight"
  let sequence = "V(3D)NK(3F)NKEXCNZRAIEUALDPNLNDQQFHUKIWZIIXDC"
  let weight = 2194.9
  let results = calculationResults weight sequence
  let result = take 1 [ seq | (seq,wt) <- results ]
  maybe (writeBS "must specify params in URL")
         writeJSON result

The error from the compiler is:

Couldn't match expected type `Maybe a0'
                with actual type `[([Char], Float)]'

So I need to figure out how to deal with Maybe. I'm reading on it but some help would be appreciated.


Solution

  • writeJSON has the type

    writeJSON :: (MonadSnap m, ToJSON a) => a -> m ()
    

    where the a and the m must be resolved to specific types before the compiler can run the code. You can certainly use your object of type [(String, Double)] as the input because String has an instance of ToJSON, as does Double, as do any pair of things with ToJSON instances (i.e. instance (ToJSON a, ToJSON b) => ToJSON (a, b)), and finally as do lists of things with ToJSON instances (i.e. instance ToJSON v => ToJSON [a]).

    So the problem is entirely in trying to determine what m is. The compiler is complaining that as long as m is "anything" it cannot be sure that it's an instance of MonadSnap. We can reassure the type checker at least by just asserting it is so

    >>> :t writeJSON [("foo", 3)] :: MonadSnap m => m ()
    writeJSON [("foo", 3.0)] :: MonadSnap m => m ()
    

    But we need to pick a concrete choice of m before we can run it. If you have a test application which uses MonadSnap then you can embed this line of code there, for instance.

    For testing, however, you probably should just use the function that writeJSON depends upon: encode from Data.Aeson.

    >>> encode [("foo", 3.0)]
    "[[\"foo\",3.0]]"