"Hello" |> printfn generates an error in F# - function

https://tryfsharp.fsbolero.io/
printfn "Hello"
works as expected without errors, however, using pipe operator
"Hello" |> printfn
The type 'string' is not compatible with the type 'Printf.TextWriterFormat'
I understood the pipe operator behavior:
f(a) is equivalent to a |> f
Why does the latter generate the error?? Thanks.

Yes. The pipe operator does what you think it does. However, printfn is "special" that it takes a kind of "formattable string" (there are different kinds of these) and the compiler does its magic only when the format string appears as a direct argument.
In other words, your "Hello" in the first example is not really a string, it is a Printf.TextWriterFormat object, magically created by the compiler.
Still, you can do what you want by using an explict format string. This approach you'll see quite a bit in real-world code:
"Hello" |> printfn "%s"
Here, %s means: give me a string. The magic of F# in this case again takes the format string, here "%s", this time with an argument of type string, and turns it into a function.
Note 1: that this "surprise effect" is being considered and the community works towards adding a printn function (i.e., without the f which stands for format) to just take a simple string: https://github.com/fsharp/fslang-suggestions/issues/1092
Note 2: if you do want to pass arguments to printfn around, you can make the type explicit, but this is done very rarely:
let x = Printf.TextWriterFormat<unit> "Hello"
x |> printfn // now it's legal
Note 3: you might wonder why the compiler doesn't apply its magic to the lh-side of |> as well. The reason is that |> is not an intrinsic to the compiler, but just another overridable operator. Fixing this is therefor technically very hard (there can be any amount of operators on the lh-side), though it has been considered at certain times.
Other alternative
In the comments, the OP suggested that he/she/they didn't like the idea of having to use printfn "%i" and the like, and ended up writing print, printInt etc functions.
If you go that route, you can write your code in a class with only static methods, while using function-style naming. Then just open the static type (this is a new feature in F# 6).
module MyPrinters =
// create a static type (no constructors)
type Printers =
static member print x = printfn "%s" x // string
static member print x = printfn "%i" x // integer
static member print x = printfn "%M" x // decimal
static member print x = printfn "%f" x // float
module X =
// open the static type
open type MyPrinters.Printers
let f() = print 42
let g() = print "test"

Related

Use of typeclasses in the JSON example form Real World Haskell

I have found Real World Haskell - Chapter 5 mainly confusing, and it seems it is stinging me also in Chapter 6.
In Chapter 6, up to just before Typeclasses at work: making JSON easier to use, everything seems clear to me; then the book shows a portion of a JSON file, as well as a Haskell source that defines a variable result holding (most of) that JSON example.
Then, with reference to the preceding chunk of code, it makes the difference between JSON objects, which can contain elements of different types, and Haskell lists, which cannot. This justifies the use of JValue's constructors (JNumber, JBool, ...) in the aforementioned code.
So far so good. Then it starts becoming confusing form me.
This limits our flexibility: if we want to change the number 3920 to a string "3,920", we must change the constructor that we use to wrap it from JNumber to JString.
Yeah, so what? If my intention is to make this change, I will have to change, for instance, this line
("esitmatedCount", JNumber 3920)
to this
("esitmatedCount", JString "3,920")
which corresponds to changing 3920 to "3,920" in the actual JSON file. So what? If I had the chance of putting different types in a Haskell list, I would still have to wrap the number in the double quotes and add the comma.
I don't get where the loss of flexibility is.
Then a tempting solution is proposed (tempting? Than it is no good... Where's the drawback? Some comments in the online book pose the same question.)
type JSONError = String
class JSON a where
toJValue :: a -> JValue
fromJValue :: JValue -> Either JSONError a
instance JSON JValue where
toJValue = id
fromJValue = Right
Now, instead of applying a constructor like JNumber to a value to wrap it, we apply the toJValue function. If we change a value's type, the compiler will choose a suitable implementation of toJValue to use with it.
This makes me thing that the intention is to use the toJValue function instead of the constructor JNumber, but I don't see how toJValue 3920 could work.
How should I make use of the code above?
Update: I added answers to your follow-up comments in a section at the end.
I think the authors, in writing the type classes chapter, wanted to maintain continuity with the example from the previous chapter. They may also have had in mind some real code they'd written where type classes were used for working with JSON in Haskell. (I see that Bryan O'Sullivan, one of the authors of RWH, is also the author of the excellent aeson JSON parsing library which uses type classes extremely effectively.) I think that they were also a little frustrated that their best example of the need for type classes (BasicEq) was something that was already implemented, which forced readers to pretend that the language designers had left a key feature out of the language in order to see the need for type classes. They also realized that the JSON example was rich and complex enough to allow them to introduce some difficult new concepts (type synonym and overlapping instances, the open world assumption, newtype wrappers, etc.).
So, they tried to add the JSON example as a realistic, reasonably complex example that related back to the earlier material and could be used for pedagogical purposes to introduce a bunch of new material.
Unfortunately, they realized a little too late that the motivation for the example was weak, at least without introducing a bunch of advanced new concepts and techniques. So, they mumbled something about "lack of flexibility", forged ahead anyway, and let the example peter out at the end, never really getting back to how someone would use toJValue or fromJValue for anything.
Here's a demonstration of why the JSON class is useful, motivated by the aeson package. Note that it uses several more advanced features that haven't been covered by the first five chapters of RWH, so you might not be able to follow it all yet.
So we're on the same page, suppose we have the following code, a slightly simplified version of the type class and instances from Chapter 6. There are a few extra the language extensions here that are needed for the code below.
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RankNTypes, RecordWildCards #-}
module JSONClass where
data JValue = JString String
| JNumber Double
| JBool Bool
| JNull
| JObject [(String, JValue)]
| JArray [JValue]
deriving (Show)
class JSON a where
toJValue :: a -> JValue
fromJValue :: JValue -> Maybe a
instance JSON Bool where
toJValue = JBool
fromJValue (JBool b) = Just b
fromJValue _ = Nothing
instance {-# OVERLAPPING #-} JSON String where
toJValue = JString
fromJValue (JString s) = Just s
fromJValue _ = Nothing
instance JSON Double where
toJValue = JNumber
fromJValue (JNumber x) = Just x
fromJValue _ = Nothing
instance {-# OVERLAPPABLE #-} (JSON a) => JSON [a] where
toJValue = JArray . map toJValue
fromJValue (JArray vals) = mapM fromJValue vals
fromJValue _ = Nothing
Also suppose that we have some Haskell data types representing search results, patterned after the result example given in the "Typeclasses at work" section:
data Search = Search
{ query :: String
, estimatedCount :: Double
, moreResults :: Bool
, results :: [Result]
} deriving (Show)
data Result = Result
{ title :: String
, snippet :: String
, url :: String
} deriving (Show)
It would be nice to convert these to JSON. Using the RecordWildCards extension which "blows up" the fields of a parameter into separate variables, we can write this quite cleanly:
resultToJValue :: Result -> JValue
resultToJValue Result{..}
= JObject [("title", JString title), ("snippet", JString snippet), ("url", JString url)]
searchToJValue :: Search -> JValue
searchToJValue Search{..}
= JObject [("query", JString query),
("estimatedCount", JNumber estimatedCount),
("moreResults", JBool moreResults),
("results", JArray $ map resultToJValue results)]
It's a little cluttered with constructors. We could "simplify" this by replacing some of the constructors with toJValue which would give us:
resultToJValue :: Result -> JValue
resultToJValue Result{..}
= JObject [("title", toJValue title), ("snippet", toJValue snippet),
("url", toJValue url)]
searchToJValue :: Search -> JValue
searchToJValue Search{..}
= JObject [("query", toJValue query),
("estimatedCount", toJValue estimatedCount),
("moreResults", toJValue moreResults),
("results", JArray $ map resultToJValue results)]
You could easily argue that's really no less cluttered. However, the type class allows us to define a helper function:
(.=) :: (JSON a) => String -> a -> (String, JValue)
infix 0 .=
k .= v = (k, toJValue v)
which introduces a nice, clean syntax:
resultToJValue :: Result -> JValue
resultToJValue Result{..}
= JObject [ "title" .= title
, "snippet" .= snippet
, "url" .= url ]
searchToJValue :: Search -> JValue
searchToJValue Search{..}
= JObject [ "query" .= query
, "estimatedCount" .= estimatedCount
, "moreResults" .= moreResults
, ("results", JArray $ map resultToJValue results)]
and the only reason the last line looks ugly is that we didn't give Result its JSON instance:
instance JSON Result where
toJValue = resultToJValue
which would allow us to write:
searchToJValue :: Search -> JValue
searchToJValue Search{..}
= JObject [ "query" .= query
, "estimatedCount" .= estimatedCount
, "moreResults" .= moreResults
, "results" .= results ]
In fact, we don't need the functions resultToJValue and searchToJValue at all, as their definitions can just be given directly in the instances. So, all the code above after the definitions of the Search and Result data types can be collapsed down to:
(.=) :: (JSON a) => String -> a -> (String, JValue)
infix 0 .=
k .= v = (k, toJValue v)
instance JSON Result where
toJValue Result{..}
= JObject [ "title" .= title
, "snippet" .= snippet
, "url" .= url ]
instance JSON Search where
toJValue Search{..}
= JObject [ "query" .= query
, "estimatedCount" .= estimatedCount
, "moreResults" .= moreResults
, "results" .= results ]
which provides support for:
search = Search "awkward squad haskell" 3920 True
[ Result "Simon Peyton Jones: papers"
"Tackling the awkward squad..."
"http://..."
]
main = print (toJValue search)
What about converting a JSON JValue back to Result and Search? You might want to try writing this up without using the type classes and see what it looks like. The solution with type classes uses a mind-bending helper function (that requires the RankNTypes language extension):
withObj :: (JSON a) => JValue ->
((forall v. JSON v => String -> Maybe v) -> Maybe a) -> Maybe a
withObj (JObject lst) template = template v
where v k = fromJValue =<< lookup k lst
after which the instances are easily written using applicative syntax (<$> and <*>) which allow us to combine a bunch of Maybe values as paramters to a function call, returning Nothing if any of the parameters are Nothing (i.e., unexpected type in the JSON) and calling the function otherwise:
instance JSON Result where
fromJValue o = withObj o $ \v -> Result <$> v "title" <*> v "snippet" <*> v "url"
instance JSON Search where
fromJValue o = withObj o $ \v -> Search <$> v "query" <*> v "estimatedCount"
<*> v "moreResults" <*> v "results"
Without type classes, this sort of uniform treatment of different field types using the helper functions (.=) and withObj wouldn't have been possible and the final syntax for writing these marshalling functions would have been substantially more complicated.
This example couldn't have been introduced as-is in RWH Chapter 6, as it involves applicatives (the <*> syntax), higher rank types (withObj), and probably a bunch of other things I've forgotten about. I'm not sure if it could be simplified enough to make the final syntax look nice enough that the advantages of using type classes would be clear.
Anyway, here's the full code. You might want to browse the documentation for the aeson package to see what a real library based on this approach would look like.
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RankNTypes, RecordWildCards #-}
module JSONClass where
-- JSON type
data JValue = JString String
| JNumber Double
| JBool Bool
| JNull
| JObject [(String, JValue)]
| JArray [JValue]
deriving (Show)
-- Type classes and instances
class JSON a where
toJValue :: a -> JValue
fromJValue :: JValue -> Maybe a
instance JSON Bool where
toJValue = JBool
fromJValue (JBool b) = Just b
fromJValue _ = Nothing
instance {-# OVERLAPPING #-} JSON String where
toJValue = JString
fromJValue (JString s) = Just s
fromJValue _ = Nothing
instance JSON Double where
toJValue = JNumber
fromJValue (JNumber x) = Just x
fromJValue _ = Nothing
instance {-# OVERLAPPABLE #-} (JSON a) => JSON [a] where
toJValue = JArray . map toJValue
fromJValue (JArray vals) = mapM fromJValue vals
fromJValue _ = Nothing
-- helpers
(.=) :: (JSON a) => String -> a -> (String, JValue)
infix 0 .=
k .= v = (k, toJValue v)
withObj :: (JSON a) => JValue ->
((forall v. JSON v => String -> Maybe v) -> Maybe a) -> Maybe a
withObj (JObject lst) template = template v
where v k = fromJValue =<< lookup k lst
-- our new data types
data Search = Search
{ query :: String
, estimatedCount :: Double
, moreResults :: Bool
, results :: [Result]
} deriving (Show)
data Result = Result
{ title :: String
, snippet :: String
, url :: String
} deriving (Show)
-- JSON instances to marshall them in and out of JValues
instance JSON Result where
toJValue Result{..}
= JObject [ "title" .= title
, "snippet" .= snippet
, "url" .= url ]
fromJValue o = withObj o $ \v -> Result <$> v "title" <*> v "snippet" <*> v "url"
instance JSON Search where
toJValue Search{..}
= JObject [ "query" .= query
, "estimatedCount" .= estimatedCount
, "moreResults" .= moreResults
, "results" .= results ]
fromJValue o = withObj o $ \v -> Search <$> v "query" <*> v "estimatedCount"
<*> v "moreResults" <*> v "results"
-- a test
search :: Search
search = Search "awkward squad haskell" 3920 True
[ Result "Simon Peyton Jones: papers"
"Tackling the awkward squad..."
"http://..."
]
main :: IO ()
main = do
let jsonSearch = toJValue search
print jsonSearch
let search' = fromJValue jsonSearch :: Maybe Search
print search'
Answers to Questions from the Comments
You asked a bunch of follow-up questions in the comments. I've tried to answer them here, in slightly different order:
Q: The book uses Either and you use Maybe. I would say that's just because you are using Nothing to say something went wrong and the book suggests using an explanatory String to give details about the wrong. Ok, but the book's definition of toJValue and fromJValue are so different from yours: I can't see how toJValue = id can be useful as the type of input and output cannot be different, based on id's signature; and fromJValue, given any JValue returns Right that JValue, whereas you deconstruct to return the Haskell type wrapped in it.
A: Yes, I used Maybe instead of Either to signal errors, and only because I thought it made my example a little bit simpler. The book talks about this in the "More helpful errors" section, noting that Maybe could have been used, too, but Either allows more helpful error messages to be provided: the book's Left is like my Nothing but with an additional explanatory note.
Maybe my plan to simplify things backfired because my version is supposed to look similar to the book's version. I think you're just comparing the wrong instances. Consider the class definitions first:
-- from book
class JSON a where
toJValue :: a -> JValue
fromJValue :: JValue -> Either JSONError a
-- mine
class JSON a where
toJValue :: a -> JValue
fromJValue :: JValue -> Maybe a
The only intended difference here is that fromJValue can return Left errmsg or Right answer in the book's version versus Nothing or Just answer in my version. For a particular instance, like the Bool instance, we have:
-- from book
instance JSON Bool where
toJValue = JBool
fromJValue (JBool b) = Right b
fromJValue _ = Left "not a JSON boolean"
-- mine
instance JSON Bool where
toJValue = JBool
fromJValue (JBool b) = Just b
fromJValue _ = Nothing
Again, these match except that Right becomes Just and Left "message" becomes Nothing. I think all that's throwing you off is that the book defines this extra instance for the JValue type:
instance JSON JValue where
toJValue = id
fromJValue = Right
that I decided not to define because I didn't need it for anything. This instance is weird and different from all the other instances. All the other instances involve translating other Haskell types to and from a corresponding JValue representation. This instance "translates" a JValue to and from itself. So, toJValue is just id because no transformation is actually needed. For fromJValue, we'd be tempted to use id, too, but the general fromJValue function is allowed to fail by returning Left errmsg (or Nothing, for my version) if the types don't match. But, a JValue is always the right type to be "translated" to a JValue, so we can always return a Right answer. My version of this instance would look like:
instance JSON JValue where
toJValue = id
fromJValue = Just -- use Just instead of Right; we never return Nothing
Q: Also, you put fromJValue _ = Nothing in every instance, whereas the book doesn't even mention Left, not before "More Helpful Errors". Maybe I have to keep reading a bit, as understanding half of your answer has hopefully unlocked something in my understanding.
A: Well, the book only introduces one instance before the "More helpful errors" section, and it's that instance JSON JValue. My version wouldn't have needed Nothing, the same way the book's didn't need Left. As soon as we start defining instances that can fail, then we need either Left or Nothing.
Q: The other concerns mapM: why is that used? I understand that vals is a valid list in input to the JArray constructor (otherwise we couldn't be on the lines which deconstructs it, right?), so applying fromJValue to each element of the list should return a Maybe (all Just, right?) just as the signature of the function requires.
A: Yes, you're correct. You're just missing one step. To be concrete, let's say we're trying to read a Haskell list of doubles ([Double]) from a JSON value like:
JArray [JNumber 1.0, JNumber 2.0, JNumber 3.0]
so we have vals = [JNumber 1.0, JNumber 2.0, JNumber 3.0]. As you say, we want to apply fromJValue to each element of this list. If we did that using map, like:
map fromJValue vals
we'd get:
[Just 1.0, Just 2.0, Just 3.0] :: [Maybe Double]
but that return value doesn't actually match the type signature. That's a [Maybe Double] value, but we want a Maybe [Double] value, more like:
Just [1.0, 2.0, 3.0] :: Maybe [Double]
The purpose of mapM here is to pull that "Just" outside of the list. It serves a second purpose, too. If we were trying to read a list like:
[JNumber 1.0, JNumber 2.0, JString "three point zero"]
then applying map fromJValue vals (in a context where fromJValue has been specialized to the Double instance) would give:
[Just 1.0, Just 2.0, Nothing] :: [Maybe Double]
Here, even though some of the elements were successfully transformed to doubles, some couldn't be, so we actually want to indicate overall failure by transforming that whole thing into the final result:
Nothing :: Maybe [Double]
The mapM function is a general monadic map, but for the specific monad I'm using (the Maybe monad), it has signature:
mapM :: (a -> Maybe b) -> [a] -> Maybe [b]
It's best understood as using a function a -> Maybe b that can "succeed" by returning Just or "fail" by returning Nothing, and applying that function to a list [a]. If all applications succeed, it returns Just the list of the results (pulling the Just outside the list); if any fail, it returns a global Nothing failure value.
It's actually the same idea as the function mapEithers in RWH. That function applies a function to the list [a], and if they all succeed (by returning Rights), it returns Right the list of the results (pulling the Right outside the list); if any fail (by returning Left), it returns a Left failure value (using the "first" error message encountered, if multiple errors are generated). In fact, mapEithers didn't need to be defined. It could have been replaced with mapM, because mapM works with both the Maybe monad and the Either errmsg monads and has the same behavior as mapEithers for the Either errmsg monad.
Q: One concerns JNull, which is the only constructor not taking any parameters, so there's nothing to deconstruct and, therefore, no type that could be made an instance of JSON. How does this fit the overall picture?
A: The most literal way of translating JNull would be to translate it into a Haskell type that contains no information. There actually is such a type. It's named "unit" and written () in source code. You've probably seen it used in various contexts. The appropriate instance would be:
instance JSON () where
toJValue () = JNull
fromJValue JNull = Just ()
fromJValue _ = Nothing
I didn't include this instance because it's pretty useless. It would only be helpful for translating to and from some JSON where a particular field always had the explicit value null, but that's not the way null is used in JSON.
At the risk of complicating things further, here is a potential use for JNull. Suppose I wanted to add an optional field to my Result type, for the URL of a "favorites icon" or something.
data Result = Result
{ title :: String
, snippet :: String
, url :: String
, favicon :: Maybe String -- new, optional field
} deriving (Show)
Now, my beautiful instance JSON Result is broken, because I don't have a way of handling a Maybe String field. But, I can introduce a new instance to handle Maybe values that uses JNull as the equivalent of Nothing:
instance JSON a => JSON (Maybe a) where
toJValue Nothing = JNull
toJValue (Just x) = toJValue x
fromJValue JNull = Just Nothing
fromJValue x = Just <$> fromJValue x
There are many complicated things going on here, and I won't try to explain them all. However, it may help to understand that the return value of fromJValue is the rather weird type Maybe (Maybe a) where the two Maybes serve different purposes: the outer Maybe says whether the transformation succeeded, and the inner Maybe says whether the optional value was available or missing. So Just Nothing is a value representing a successful transformation to a missing value!
The point of that instance is that we can update our Result instance to include the new field:
instance JSON Result where
toJValue Result{..}
= JObject [ "title" .= title
, "snippet" .= snippet
, "url" .= url
, "favicon" .= favicon ]
fromJValue o = withObj o $ \v -> Result <$> v "title" <*> v "snippet"
<*> v "url" <*> v "favicon"
and the instance can now (sort of) handle an optional value:
> toJValue (Result "mytitle" "mysnippet" "myurl" Nothing)
JObject [("title",JString "mytitle"),("snippet",JString "mysnippet"),("url",JString "myurl"),("favicon",JNull)]
> toJValue (Result "mytitle" "mysnippet" "myurl" (Just "myfavicon"))
JObject [("title",JString "mytitle"),("snippet",JString "mysnippet"),("url",JString "myurl"),("favicon",JString "myfavicon")]
though in generating real JSON, you probably would leave out any fields that looked like { ..., favicon: null, ... }, so you'd want to introduce some filtering to remove null fields from the final JSON value. Also, fromJValue doesn't actually handle a truly missing optional field:
> fromJValue (JObject [("title",JString "mytitle"),("snippet",JString "mysnippet"),("url",JString "myurl")]) :: Maybe Result
Nothing
Instead, it needs an explicit JNull to work correctly:
> fromJValue (JObject [("title",JString "mytitle"),("snippet",JString "mysnippet"),("url",JString "myurl"),("favicon",JNull)]) :: Maybe Result
Just (Result {title = "mytitle", snippet = "mysnippet", url = "myurl", favicon = Nothing})
so we'd need to do a bit more coding to get this to work properly.

Elm 'Json.Decode.succeed': how is it used in a decode pipeline if it is supposed to always return the same value?

I'm learning Elm and one thing that has puzzled me is 'Json.Decode.succeed'. According to the docs
succeed : a -> Decoder a
Ignore the JSON and produce a certain Elm value.
decodeString (succeed 42) "true" == Ok 42
decodeString (succeed 42) "[1,2,3]" == Ok 42
decodeString (succeed 42) "hello" == Err ...
I understand that (although, as a beginner, I don't yet see its use). But this method is also used in a Decode pipeline, thus:
somethingDecoder : Maybe Wookie -> Decoder Something
somethingDecoder maybeWookie =
Json.Decode.succeed Something
|> required "caterpillar" Caterpillar.decoder
|> required "author" (Author.decoder maybeWookie)
What is going on here? That is, if 'succeed' ignores the JSON that's passed to it, how is it used to read JSON and turn it into Elm values? Any clues appreciated!
Just to start, the intuition for a decoder pipeline is that it acts like a curried function where piping with required and optional applies arguments one-by-one. Expect that everything, both the function, its arguments and the return value are all wrapped in Decoders.
So as an example:
succeed Something
|> required (succeed 42)
|> required (succeed "foo")
is equivalent to
succeed (Something 42 "foo")
and
decodeString (succeed (Something 42 "foo")) whatever
will return Ok (Something 42 "foo") as long as whatever is valid JSON.
When everything succeeds it's just a really convoluted function call. The more interesting aspect of decoders, and the reason we use them in the first place, is in the error path. But since 'succeed' is what's of interest here, we'll ignore that and save a lot of time, text and brain cells. Just know that without considering the error path this will all seem very contrived.
Anyway, let's try to recreate this to see how it works.
Decode.map2
The key to the pipelines, apart form the pipe operator, is the Decode.map2 function. You've probably already used it, or its siblings, if you've tried writing JSON decoders without using pipelines. We can implement our example above using map2 like this:
map2 Something
(succeed 42)
(succeed "foo")
This will work exactly like the example above. But the problem with this, from a user POV, is that if we need to add another argument we also have to change map2 to map3. And also Something isn't wrapped in a decoder, which is boring.
Calling functions wrapped in Decoders
The reason this is useful anyway is because it gives us access to several values at the same time, and the ability to combine them in whatever way we want. We can use this to call a function inside a Decoder with an argument inside a Decoder and have the result also wrapped in a Decoder:
map2 (\f x -> f x)
(succeed String.fromInt)
(succeed 42)
Currying and partial application
Unfortunately this still has the problem of needing to change the map function if we need more arguments. If only there was a way to apply arguments to a function one at a time... like if we had currying and partial application. Since we have a way to call functions wrapped in decoders now, what if we return a partially applied function instead and apply the remaining arguments later?
map2 (\f x -> f x)
(succeed Something)
(succeed 42)
will return a Decoder (string -> Something), so now we just have to rinse and repeat with this and the last argument:
map2 (\f x -> f x)
(map2 (\f x -> f x)
(succeed Something)
(succeed 42))
(succeed "")
Et voila, we have now recreated JSON decode pipelines! Although it might not look like it on the surface.
Ceci n'est pas une pipe
The final trick is to use map2 with the pipe operator. The pipe is essentially defined as \x f -> f x. See how similar this looks to the function we've been using? The only difference is that the arguments are swapped around, so we need to swap the order we pass arguments as well:
map2 (|>)
(succeed "")
(map2 (|>)
(succeed 42)
(succeed Something))
and then we can use the pipe operator again to reach the final form
succeed Something
|> map2 (|>)
(succeed 42)
|> map2 (|>)
(succeed "")
It should now be apparent that required is just an alias for map2 (|>).
And that's all there is to it!

Declaration of functions inside a module OCaml

So, i'am learning OCaml step by step and i've recently met the great world of "module langage" in OCaml.
Here is my problem:
module type TEST = sig
val addend : 'a -> list
end
module Test : TEST =
struct
let addend (val,lol) =
val::lol
end
when i try to use this inside the interpretor it tells me that i have a syntax error ...
I know that's this kind of anoying question asked by this guy who's a noob and thinks stack overflow is here to rewrite is poorly optimised code but still, after hours spend searching for a solution i've almost gived up at this point...
OCaml is cool, but why is it so hard.
First, in the definition of the type of your module : you have to specify the type of element of your list. You cannot use list alone.
The following code will compile :
module type TEST = sig
val addend : 'a -> 'a list
end;;
But will not do what you want as you want to define later a function that takes a element, a list and puts this element in this list.
So the signature of such function is :
'a -> 'a list -> 'a list
So the type of your module shall be :
module type TEST = sig
val addend : 'a -> 'a list -> 'a list
end;;
And the implementation :
module Test : TEST =
struct
let addend a l = a::l
end
;;
Please note that in your code, you may be influenced by C like language where you pass the arguments in parenthesis. In Ocaml, parenthesis are used for tuples ; this means that your implementation was different from the signature.
You get a syntax error becaue val is a keyword. You cannot use it as an identifier.

How do I use the json library?

I'm trying to figure out Haskell's json library. However, I'm running into a bit of an issue in ghci:
Prelude> import Text.JSON
Prelude Text.JSON> decode "[1,2,3]"
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`JSON a' arising from a use of `decode' at <interactive>:1:0-15
Probable fix: add a type signature that fixes these type variable(s)
I think this has something to do with the a in the type signature:
decode :: JSON a => String -> Result a
Can someone show me:
How to decode a string?
What's going with the type system here?
You need to specify which type you want to get back, like this:
decode "[1,2,3]" :: Result [Integer]
-- Ok [1,2,3]
If that line was part of a larger program where you would go on and use the result of decode the type could just be inferred, but since ghci doesn't know which type you need, it can't infer it.
It's the same reason why read "[1,2,3]" doesn't work without a type annotation or more context.
The decode function is defined as follows:
decode :: JSON a => String -> Result a
In a real program, the type inference engine can usually figure out what type to expect from decode. For example:
userAge :: String -> Int
userAge input = case decode input of
Result a -> a
_ -> error $ "Couldn't parse " ++ input
In this case, the type of userAge causes the typechecker to infer that decode's return value, in this particular case, is Result Int.
However, when you use decode in GHCi, you must specify the type of the value, e.g.:
decode "6" :: Result Int
=> Ok 6
A quick glance at the docs seems to suggest that the purpose of this function is to allow you to read JSON into any Haskell data structure of a supported type, so
decode "[1, 2, 3]" :: Result [Int]
ought to work

Writing an instance of liftIO for Text.JSON's Result type

Haskell's Text.JSON library uses an abstract data type called Result, it's basically their form of Maybe, but instead of Nothing, there's Error String. Anywho, I need to use liftIO to convert a function call returning an IO thing to a Result thing inside of my implementation of JSON.readJSON. I'm new to monad transformers and can't seem to implement liftIO for Result (I keep trying to construct the infinite type, according to ghci).
Any ideas?
Many thanks
EDIT
Sorry it's taken me so long to elaborate! I appreciate your help guys.
readJSON (JSObject obj) = do text <- getVal obj "text"
user <- getVal obj "from_user"
iden <- getVal obj "id_str"
url <- (do if (length.extractURLs) text == 0
then return ""
else return $ head $ extractURLs text)
title <- liftIO (getSiteTitle url)
return $
Tweet
NewsStory {
title = "Twitter",
desc = text,
url = url,
metric = 0,
sourceURL = "twitter.com/" ++ user ++ "/status/" ++ iden
}
So the last line before return uses getSiteTitle to parse a website at that url for its title. However, that function returns a type of IO String and the compiler tells me it wants the it to be Result. Is this impossible?
Thanks again!
EDIT2
I've decided to eliminate the title from my data type and get it later on when inside an IO monad. Thanks for everyone's help! I've certainly learned from this issue.
You can't use IO inside of readJSON (without resorting to unsafePerformIO). liftIO is used when you have a stack of monad transformers with IO at bottom. Perhaps if you give more concrete information of what you are trying to achieve you'll be able to get a more useful answer :)
What you want is impossible. In the json package, readJSON :: JSValue -> Result a. By definition, this is a pure function. Arguably, you could instantiate the result to something with IO inside, but then it would be impossible to fulfill showJSON :: a -> JSValue.
My solution? I'd give your data type a title :: Maybe String and then have a second pass in IO that filled in titles.
You don't say which particular JSON library your are using and there are dozens of them with similar names and namespaces...
Because you want to use IO to get the url you will have to divide your function into two functions, one in the JSON monad and one in the IO monad. Because you cannot "run" the IO monad, you will have to run the JSON monad within IO, here doAll is a further function to combine the two.
You will have to edit it a little to make it match the JSON library you are using - some of the type signatures are not filled out and I don't know what the "run" function and return types of your JSON monad:
-- This one is in the JSON monad...
-- The type sig needs fixing...
readJSON :: JSObject ?? -> GetJSON (String,String,String,String)
readJSON (JSObject obj) = do
text <- getVal obj "text"
user <- getVal obj "from_user"
iden <- getVal obj "id_str"
url <- (do if (length.extractURLs) text == 0
then return ""
else return $ head $ extractURLs text)
return (text,user,iden,url)
-- This one is in IO...
ioStep :: (String,String,String,String) -> IO TweetNewsStory
ioStep (text,user,iden,url) = do
title <- getSiteTitle url
return $ TweetNewsStory {
title = "Twitter",
desc = text,
url = url,
metric = 0,
sourceURL = "twitter.com/" ++ user ++ "/status/" ++ iden
}
-- Type sig needs fixing...
-- The JSON library will provide something similar to
-- runJSON...
--
doAll :: JSObject ?? -> IO TweetNewsStort
doAll jsobj =
let ans = runJSON $ readJSON jsobj in
case ans of
Left err -> error $ err
Right val -> ioStep val