Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Exercise #07 + #08] Discussion and Help. #7

Closed
alpacaaa opened this issue Apr 15, 2019 · 2 comments
Closed

[Exercise #07 + #08] Discussion and Help. #7

alpacaaa opened this issue Apr 15, 2019 · 2 comments
Labels
no stupid questions When going through the exercises, ask your questions here!

Comments

@alpacaaa
Copy link
Owner

alpacaaa commented Apr 15, 2019

This issue is specific for discussion on Exercise #07 and #08.

You can ask anything, there are no stupid questions!

Did you find the exercise difficult?
Did the tests make sense?
Was the goal clear?

@alpacaaa alpacaaa added no stupid questions When going through the exercises, ask your questions here! and removed no stupid questions When going through the exercises, ask your questions here! labels Apr 15, 2019
@alpacaaa alpacaaa changed the title [Exercise #07] Discussion and Help. [Exercise #07 + #08] Discussion and Help. Apr 28, 2019
@robertlyson
Copy link

Thanks for sharing these exercises, super cool 👍.

With #8 I came up with a little bit diff. model so it made insert part simpler:

data Item
    = Item { model :: String, quantity :: Int }
    deriving (Eq, Show, Generic, Aeson.FromJSON, Aeson.ToJSON)

data ShoppingCart
    = ShoppingCart (Map.Map String Int) 
    deriving (Eq, Show, Generic, Aeson.FromJSON, Aeson.ToJSON)
updateCartHandler :: ShoppingCart -> Server.Request -> (ShoppingCart, Server.Response)
updateCartHandler (ShoppingCart cart) req = 
    case Server.decodeJson body of 
        Right item ->
            (appendToCart item, Server.stringResponse "ok")
        Left err ->
            (ShoppingCart cart, Server.failureResponse $ "Got an error! " ++ show err)
    where 
        body = Server.requestBody req 
        appendToCart item =
            ShoppingCart (Map.insertWith (+) (model item) (quantity item) cart)

but on the other hand, retrieving shopping cart items is cumbersome now because of these mappings:

getCartHandler :: ShoppingCart -> Server.Request -> (ShoppingCart, Server.Response)
getCartHandler (ShoppingCart cart) _ =
    (ShoppingCart cart, Server.jsonResponse $ sortOnQuantity $ List.map toItem $ Map.toList cart) 
    where 
        sortOnQuantity items =
            List.reverse $ List.sortOn quantity items 
        toItem (x,y) = Item { model = x, quantity = y }

@alpacaaa
Copy link
Owner Author

alpacaaa commented May 1, 2019

Hey @robertlyson!

I like where you're going with this -- Map.insertWith is indeed the right function to use in this case.
Will change the exercise a bit (and my solution) because as it stands it's not very clear that the better approach is to use two Maps: [itemId -> item] and [itemId -> quantity].

My previous reply (not particularly beginner friendly 💩) As you noted, it's still a bit unergonomic. This is where lenses would come quite useful. With the `generic-lens` package we could write something like this:
-- Provided that we revert the ShoppingCart definition.
data ShoppingCart
    = ShoppingCart (Map.Map String Item) 
    deriving (Eq, Show, Generic, Aeson.FromJSON, Aeson.ToJSON)


appendToCart item
    = ShoppingCart
    $ Map.insertWith
           (\existing new -> existing & field @"quantity" ~+ (quantity new))
           (model item)
           item
           cart

There's a lot to unpack here.

& is just function application, you can find it in base.

The ~+ operator is defined in the lens package. It's pretty much the equivalent of a += b in any other language. Lastly, that funky @"quantity" is a type application.

I appreciate it looks scary so don't worry too much about it. I think your solution is absolutely fine!

I generally don't reach for lenses and I hate it when people go crazy with them because I can't understand what's going on (ie. the classic Haskell bullshit that I want to avoid here). I find it quite ironic that my first comment on ZBH is about lenses :)

Good job getting to Exercise 08, I hope to add more soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no stupid questions When going through the exercises, ask your questions here!
Projects
None yet
Development

No branches or pull requests

2 participants