1

Here is the definition for a Json Value :

-- | A JSON value represented as a Haskell value.
data Value = Object !Object
           | Array !Array
           | String !Text
           | Number !Scientific
           | Bool !Bool
           | Null
             deriving (Eq, Show)

let value = String "myValue"
looking for => fromString value == "myValue" ??
fromString :: Value -> Text

I'm looking a function like where I could get the Text from String without to do some pattern matching, obviously this function will be unsafe... a fromString like fromJust in Data.Maybe for example... Something in Data.Lens.Aeson ?

Nicolas Henin
  • 3,244
  • 2
  • 21
  • 42

1 Answers1

2

As Thomas M. DuBuisson implies in the above comment, this sounds like an XY Problem. Still, I'll do my best to nonetheless address the specifics.

Technically, you can trivially write a function with the type Value -> Text, although it requires pattern matching. I realise that the OP requests a function without pattern matching, but please read on:

-- Warning: UNSAFE!
fromString :: Value -> Text
fromString (String s) = s

Such a function compiles, but is unsafe!

*Q53961314 Q53961314> fromString $ String "myValue"
"myValue"
*Q53961314 Q53961314> fromString $ Number 42
"*** Exception: Non-exhaustive patterns in function fromString

While it works for String values, it crashes for any other type of value. While it's technically possible to write and compile unsafe functions like the above, AFAICT it's not considered idiomatic Haskell.

A better alternative is a safe function that returns Maybe Text. This is still easy to write using pattern matching:

fromStringSafe :: Value -> Maybe Text
fromStringSafe (String s) = Just s
fromStringSafe _ = Nothing

This function is total:

*Q53961314 Q53961314> fromStringSafe $ String "myValue"
Just "myValue"
*Q53961314 Q53961314> fromStringSafe $ Number 42
Nothing

If you don't wish to write such a function yourself, but prefer to use lens-aeson, you can use the _String prism:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> String "myValue" ^? _String
Just "myValue"

As you can tell, this is also safe, in that ^? _String returns Maybe Text:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> Bool True ^? _String
Nothing
Prelude Data.Aeson Data.Aeson.Lens Control.Lens> Number 42 ^? _String
Nothing

If you really, really want an unsafe function, you can use ^?! _String:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> String "myValue" ^?! _String
"myValue"

It is, not surprisingly, unsafe:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> Number 42 ^?! _String
"*** Exception: (^?!): empty Fold
CallStack (from HasCallStack):
  error, called at src\\Control\\Lens\\Fold.hs:1285:28 in lens-4.17-7m3etWEBj0P3172qv7LEG9:Control.Lens.Fold
  ^?!, called at <interactive>:9:1 in interactive:Ghci3

I wonder why you're asking about this sort of functionality, though. Is there a specific problem you're trying to solve with Aeson that we can help with?

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Hi Mark, I think stack overflow may not be the best to discuss about it... But if you want to see what I'm doing it's here : https://github.com/Eventuria/gsd, I will be really happy that somebody challenges me on what I'm doing :-) I'm not ready yet, there is a need for documentation etc I'm warning you :-) – Nicolas Henin Dec 29 '18 at 13:40
  • 1
    @NicolasHenin You're probably right... That repo is probably a bit too high-level for a specific discussion, but after briefly spelunking around that code base, and looking at e.g. the `Cqrs.Write.Serialization.Event` module, is there any sophisticated reason I can't guess that you're writing Aeson instances by hand? – Mark Seemann Dec 29 '18 at 16:53
  • Wow lol, I'm impressed you've pointed to the right piece of code actually :-) The idea behind was to do something generic for any application on top of it...CQRS is the generic part and GSD the application... I didn't stress the code around that much till these days (I was more focusing on the infra (cqrs) than the business logic of the app (gsd)), but I'm starting to feel some pain around it...This is my first app in Haskell and I have a scala OOP background and I have fallen into some pitfalls already, so I'm totally open to feedback lol.... – Nicolas Henin Dec 30 '18 at 05:27
  • I have to do 2 logics of serialization for basically the same object which is kind of smelling fishy, one at the app level (gsd), and one at the generic level (cqrs), I think I can do better for sure... in Domain Driven Design this is what they call 2 distincts domains with an anticorruption layer between (converters basically), I'm adding more events and commands now and I'm starting to juggle with json a bit to much to my taste :-) – Nicolas Henin Dec 30 '18 at 05:27
  • I want to use that project to get a remote job in Haskell btw :-) Time is running as usual, I'm trying to get something that is usable and that I can present for interviews :-) – Nicolas Henin Dec 30 '18 at 05:29
  • I have never heard about XY problem, but you are totally right ;-), it's an XY problem .... I don't like having unsafe piece of code in what I'm doing.... What I usually do in these situations is to make the code bleeding a bit to have a couple of examples in the front of my face, then the light is usually coming and I find a better solution : naivety / pain / wiseness :-) – Nicolas Henin Dec 30 '18 at 05:41