3

I want to use string literal as Traversal, but I am a bit lost in types. Is it possible to create this instance?

import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import Data.String
import Data.Default

{- Having:
key'  :: AsValue t => Text -> Traversal' t (Maybe Value)
_JSON :: (ToJSON a, FromJSON a) => Traversal' t a
-}

instance (AsValue t, FromJSON v, ToJSON v, Default v) => IsString (Traversal' t v) where
  fromString k = key' (fromString k) . non (toJSON def) . _JSON

To achieve something like this inside State monad:

"some-key" .= (3 :: Int)

Problem with universally quantified type instances. Thanks!

jub0bs
  • 60,866
  • 25
  • 183
  • 186
Pavel
  • 127
  • 6

1 Answers1

5

I couldn't get your code to compile, but that shouldn't matter. I assume that you have a function of type

fromStringTraversal :: (AsValue t, FromJSON v, ToJSON v, Default v) 
                    => String -> Traversal' t v 
fromStringTraversal = undefined

Then to write your instance, simply inline the definition of Traversal' into the instance head. This works because any type variables in an instance are universally quantified over implicitly anyways.

{-# LANGUAGE RankNTypes, FlexibleInstances, GADTs #-}

instance (a ~ a', b ~ b', AsValue b, Default a, FromJSON a, ToJSON a, Applicative f) 
       => IsString ((a -> f a') -> b -> f b') where
  fromString = fromStringTraversal

The a ~ a', b ~ b' constraints could be moved from the context to the instance head, but this way gives better type inference. Then

{-# LANGUAGE OverloadedStrings, NoMonomorphismRestriction #-}

-- Infered type:
-- test :: (AsValue s, MonadState s m) => m ()
test = "some-key" .= (3 :: Int)
user2407038
  • 14,400
  • 3
  • 29
  • 42
  • 1
    This example also requires `RankNTypes` to talk about the lens type synonyms, `FlexibleInstances` for the `IsString` instance, either `TypeFamilies` or `GADTs` for `~`, and `hiding ((.=))` when importing `Data.Aeson`. – Cirdec Feb 13 '15 at 23:13