While trying to write a PureScript wrapper for the aws-sdk package, and in specific for the S3 interface, I was in need to parse the information coming back from the library calls.

For example, a call to listBuckets returns this kind of object:

{ Buckets: [{ Name: "String", CreationDate: "Date object" }],
Owner: { DisplayName: "String", ID: "String" }}


I was interested in the buckets array only, so at first I tried finding a solution using purescript-foreign.

I modeled my bucket data type, together with its IsForeign instance:

newtype Bucket = Bucket
{ name :: String
, creationDate :: Either String DateTime
}

instance bucketIsForeign :: IsForeign Bucket where
read value = do
name <- readProp "Name" value
creationDate <- readProp "CreationDate" value
pure \$ Bucket { name, readDate creationDate }

-- readDate is a function I write to concert js Date objects to ps DateTime objects.


I was then stuck when I tried to use my new instance to parse the bigger response object. The compiler didn’t like the fact that I wasn’t parsing the whole object. At first my solution was to write a data type representing the whole response, together with its IsForeign instance.

Thanks to this I was able to get my buckets array, but there was a lot of boilerplate involved, and I was scared that this approach would become unmanageable with more complex response objects.

I’m not sure if it’s possible to find a better solution using just purescript-foreign, but I found a nice one enough using purescript-foreign-lens.

This is the example found in the library repo:

doc :: Foreign
doc = toForeign { paras: [ { word: "Hello" }, { word: "World" } ] }

-- | This FoldP extracts all words appearing in a structure like the one above.
words :: forall r. Monoid r => FoldP r Foreign String
words = prop "paras"
<<< array
<<< traversed
<<< prop "word"
<<< string

main :: forall e. Eff (console :: CONSOLE | e) Unit
main = traverse_ log (doc ^.. words)


It parses the object, resulting in a list of strings. It was almost what I needed, I just had to find a way to replace that last string with a function that parsed buckets.

string is defined as:

string :: forall r. Monoid r => FoldP r Foreign String
string = to readString <<< traversed


readString is a function from purescript-foreign that reads foreign strings. I already had my bucketIsForeign instance defined, so I crossed my fingers and wrote:

bucket :: forall r. Monoid r => FoldP r Foreign Bucket
bucket = to read <<< traversed

buckets :: forall r. Monoid r => FoldP r Foreign Bucket
buckets = prop "Buckets"
<<< array
<<< traversed
<<< bucket


And then, luckily, I was able to get a List Bucket by using response ^.. buckets.