I am trying to create a program to manage my Prosper investments for me as an intro to Haskell project.  I created a post a few weeks ago for some of the simpler backend calculations: Recommend Next Note PurchaseNow I've started working on the web APIs in order to retrieve my account information from Prosper. Though it may not look it, the code below has already undergone some major refactoring to attempt to bring it to slightly readable.  This is my first attempt at any RESTful service interaction, JSON parsing, and usage of Lens.  I have a few areas of this code I would really appreciate some feedback.I feel like I really went overboard with the let bindings, but I couldn't figure out how to break up some absurdly long lines in another way.All my maybeXXX functions.  I needed them to operate on the Maybe typeclass in the way I was expecting, but I feel like there is a more elegant use of Monads that could avoid these auxiliary functions.The creation of the MResult type was done just to parse the response body of the web API.  There must be a way to use Lens to extract that field and parse it as a list of Note directly.Any other feedback on the general structure or style of the code is greatly appreciated.{-# LANGUAGE OverloadedStrings #-}{-# LANGUAGE DeriveGeneric #-}import Prosperimport Network.Wreqimport Control.Lensimport qualified Data.ByteString as BSimport qualified Data.ByteString.Lazy.Internal as BLimport qualified Data.Text as Timport Data.Text.Encodingimport Data.Aesonimport Data.Aeson.Lens import GHC.Genericsdata Note = Note {    loan_number :: Int,    prosper_rating :: T.Text    } deriving (Generic, Show)instance FromJSON Notedata MResult = MResult {    result :: [Note]    } deriving (Generic, Show)instance FromJSON MResult-- String ConstantsprosperAddress = https://api.prosper.com/v1/authTarget = security/oauth/tokenaccountTarget = accounts/prosper/notesTarget = notes/clientID = ciciciciciclientSecret = ssssssssssssssssuserID = uuuuuuuuuupassword = ppppppppppbody :: BS.ByteStringbody =  strToBS $            grant_type=password ++             &client_id= ++ clientID ++             &client_secret= ++ clientSecret ++             &username= ++ userID ++             &password= ++ password    where strToBS = encodeUtf8 . T.pack-- Retrives the OAuth2 token from the Prosper serveroauthToken :: IO T.TextoauthToken = do    let opts = defaults & header Accept .~ [application/json]                        & header Content-type .~ [application/x-www-    form-urlencoded]    resp <- postWith opts (prosperAddress ++ authTarget) body    return $ resp ^. responseBody . key access_token . _String-- Performs a GET request to the provided target using the provided OAuth     tokengetTarget target token = do    let tokenStr = encodeUtf8 $ T.append bearer  token    let opts = defaults & header Authorization .~ [tokenStr]    getWith opts (prosperAddress ++ target)     -- Retrives the list of currently owned notes from Prosper-- Note: The query responses are paginated to 25 results per responsegetNotesList :: T.Text -> Int -> IO [T.Text]getNotesList token offset = do    let target = notesTarget ++ ?offset= ++ (show offset)    resp <- getTarget target token    let r =  fmap result (decode $ resp ^. responseBody :: Maybe MResult)    let notes = fmap (map prosper_rating) r    if (maybeLength notes) /= 25 then        maybeReturn notes        else do            restOfNotes <- getNotesList token (offset+25)            return $ maybeAppend notes restOfNotesmaybeLength Nothing = 0maybeLength (Just xs) = length xsmaybeReturn Nothing = return []maybeReturn (Just xs) = return xsmaybeAppend :: Maybe [a] -> [a] -> [a]maybeAppend Nothing xs = xsmaybeAppend (Just ys) xs = ys ++ xsmain = do    token <- oauthToken     notes <- getNotesList token 0    print $ recommendNote notes [0,0,0.2,0.2,0.25,0.30,0.05]
Using Wreq and Lens libraries to query Prosper for account info
haskell;finance;rest;oauth
You're on the right path! You're asking great questions.I feel like I really went overboard with the let bindings, but I couldn't figure out how to break up some absurdly long lines in another way.This is sort of unavoidable with wreq (or any HTTP client library in general probably), as you have to pass around a lot of state to communicate on a stateless protocol. You do a little better if you pass your options as a single Options value. Leads to friendlier type signatures too.All my maybeXXX functions. I needed them to operate on the Maybe typeclass in the way I was expecting, but I feel like there is a more elegant use of Monads that could avoid these auxiliary functions.You can use fmap or <$> instead of constructing these maybeWobble helpers, but as we see below there's an even better option.The creation of the MResult type was done just to parse the response body of the web API. There must be a way to use Lens to extract that field and parse it as a list of Note directly.There is! Your usage of the lens-aeson package is right on the money. To grab all the prosper_rating :: Text of all the notes in the result key, we can simply write this lens traversal:_ratings = responseBody . key result                        . _Array                        . traverse                        . key prosper_rating                        . _String(Traversal just means that you need to use ^.. instead of ^. and that you'll get back a list of values instead of one when you do so.)The other way you could be using more lens is to import the text lens module, which provides two once you learn of them you can't stop using them helpers:packed, unpacked: isomorphisms between Text and String. Never import qualified Data.Text again!utf8: a prism that converts a ByteString to a Text, when possible. Never import qualified Data.Bytestring again!_Show: a prism (in Control.Lens actually, but we'll toss it onto this list) that lets you convert any Show a => a value into String. Earn a combo bonus when you compose it with packed to get a Text.One other note is that you should use param and := from the wreq library to construct parameter strings and POST bodies. They handle the text massaging for you.OK, putting everything together:{-# LANGUAGE OverloadedStrings #-}module Free_D whereimport Control.Lensimport Data.Aesonimport Data.Aeson.Lensimport Data.Monoid ((<>))import Data.Text.Strict.Lensimport Network.Wreqimport Data.Text (Text)-- String ConstantsprosperAddress, clientID, clientSecret, userID, password :: TextprosperAddress = https://api.prosper.com/v1/clientID = ciciciciciclientSecret = ssssssssssssssssuserID = uuuuuuuuuupassword = ppppppppppprosperURL :: Text -> StringprosperURL target = (prosperAddress <> target) ^. unpacked-- | Retrives the OAuth2 token from the Prosper server.oauthToken :: IO TextoauthToken = do    let opts =          defaults            & header Accept .~ [application/json]            & header Content-Type .~ [application/x-www-form-urlencoded]    let body =          [ grant_type := (password :: Text)          , client_id := clientID          , client_secret := clientSecret          , username := userID          , password := password]    resp <- postWith opts (prosperURL security/oauth/token) body    return (resp ^. responseBody . key access_token . _String)-- | Performs a GET request to the provided target using the provided-- OAuth token.getTarget :: Options -> String -> Text -> IO (Response Value)getTarget opts url token = do    getWith (opts & header Authorization .~ rawr) url >>= asJSON    where        rawr = [bearer  <> utf8 # token]-- | Retrives the list of currently owned notes from Prosper.---- Note: The query responses are paginated to 25 results per response.getNotesList :: Text -> Int -> IO [Text]getNotesList token offset = do    resp <- getTarget opts (prosperURL notes/) token    let ratings = resp ^.. _ratings    remainder <- if length ratings == 25 then return [] else getNotesList token (offset + 25)    return (ratings ++ remainder)    where        opts = defaults & param offset .~ [offset ^. re _Show . packed]        _ratings = responseBody . key result . _Array . traverse . key prosper_rating . _Stringmain :: IO ()main = do    token <- oauthToken    notes <- getNotesList token 0    print notes(I got the code compiling but, as I don't have an account, I was unable to run and test it.)