Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a record outside of an `Include'`

Tags:

haskell

ihp

I have a post that has an Include' as I've fetched it with a fetchRelated, so it has this type:

post :: Include' ["comments"] Post

If I'd like to save it, I'd get an error

post 
  |> set #title "Foo"
  |> updateRecord

The compiler will complain it's not a Post. It's an Include ... Post

My question is - can the Post be extracted out of the include, so I won't have to fetch it again from the DB?

like image 666
amitaibu Avatar asked Oct 16 '25 01:10

amitaibu


1 Answers

There's no built-in function in IHP to do this. But you can use a custom helper like this:

clearComments :: Include "comments" Post -> Post
clearComments post = updateField @"comments" (newRecord @Post).comments post

And then use it like this:

post 
  |> set #title "Foo"
  |> clearComments
  |> updateRecord

Things can get slightly more complex when we have multiple Include. The compiler will error out,

clearIncludes :: Include ["comments", "tags"] Post -> Post
clearIncludes post = post
              |> updateField @"comments" (newRecord @Post).comments
              |> updateField @"tags" (newRecord @Post).tags

We need to split this into multiple functions, each with types annotations:

clear1 :: Include' ["comments", "tags"] Post -> Include "tags" Post
clear1 post = post
       |> updateField @"comments" (newRecord @Post).comments


clear2 :: Include "tags" Post -> Post
clear2 post = post
       |> updateField @"tags" (newRecord @Post).tags

March has explained the reason:

TL;DR: updateField has a more open type signature than set and sometimes GHC needs a bit of help

In very early IHP versions updateField was actually set and it was later changed (because it was causing errors like these). The core problem with updateField is, that it's type definition is very open. It's defined as updateField :: value' -> model -> model'. Here model and model' are two independent type variables. This means a call to updateField can actually change the return type of the record (e.g. turning Post to Post "tags" LandingPage). The problem with your first version was that GHC could not figure out the model' variable because multiple updateField were chained (the model type argument is easy for GHC to figure out, it's just that the model' is independent of that).

For comparison set is defined as set :: value -> model -> model. In that case when GHC can figure out model it will never error. So set is typically easy to figure out and unlikely to error. One the other side the problem with set is that it's less flexible and cannot change the output type (e.g. with model = Post it will be set :: value -> Post -> Post, so there's no way to express e.g. the Include stuff). That's why updateField exists.

like image 129
Marc Scholten Avatar answered Oct 18 '25 18:10

Marc Scholten



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!