Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Sep 19 13:25
    codecov-io commented #432
  • Sep 19 13:03
    truizlop synchronize #432
  • Sep 19 13:03

    truizlop on io-enhancements

    Add provide method (compare)

  • Sep 19 09:56
    codecov-io commented #431
  • Sep 19 09:43
    truizlop review_requested #432
  • Sep 19 09:43
    truizlop opened #432
  • Sep 19 09:43
    truizlop review_requested #432
  • Sep 19 09:43
    truizlop labeled #432
  • Sep 19 09:41
    codecov-io commented #430
  • Sep 19 09:40

    truizlop on io-enhancements

    Rename IO methods (compare)

  • Sep 19 09:35
    truizlop labeled #431
  • Sep 19 09:35
    truizlop review_requested #431
  • Sep 19 09:35
    truizlop opened #431
  • Sep 19 09:35
    truizlop review_requested #431
  • Sep 19 09:34

    truizlop on monad-comprehensions-docs

    Add Monad comprehensions doc pa… (compare)

  • Sep 19 09:22
    truizlop review_requested #430
  • Sep 19 09:22
    truizlop review_requested #430
  • Sep 19 09:22
    truizlop opened #430
  • Sep 19 09:19

    truizlop on fix-writer

    Fix censor implementation (compare)

  • Sep 19 08:15
    codecov-io commented #429
Tomás Ruiz-López
@truizlop
Nice to hear that. Feel free to ask us if you have any questions
Tomás Ruiz-López
@truizlop
Although Twitter seems to be down at the moment
Sébastien Drode
@Dragna
Hi everyone, I've been away from my tests on Bow for some time, but I'm back 🙂 . I would like to play with reader, writer and state monad, but I don't quite understand how to use them 😓. How should I instantiate a Reader Monad with a value that would represent my environment ? and after that, how can I access it ?
Tomás Ruiz-López
@truizlop
Welcome back Sébastien!
ok so Reader<D, A> is like a plain function (D) -> A
you can usually start with calling the static method ask to get a Reader<D, D>
and map over the right argument
Tomás Ruiz-López
@truizlop
if you have an effect F, you typically need to use ReaderT<F, D, A>, which is (D) -> F<A> (namely, (D) -> Kind<F, A>
so, an example using IO:
Tomás Ruiz-López
@truizlop
Let's say I have these two protocols describing the operations I can do for network and database:
protocol Storage {
    func save(_ user: User) -> IO<Never, Bool>
}

protocol API {
    func fetch(byId: Int) -> IO<Never, User>
}
I can create an structure with the dependencies I am going to need
struct Env {
    let database: Storage
    let api: API
}
Then, I can write a function with the following signature:
func cacheUser(byId userId: Int) -> ReaderT<IOPartial<Never>, Env, Bool> which means: "Give me a user ID, and I will return a Reader that, when provided an Env will have all it needs to produce IO<Never, Bool>"
Tomás Ruiz-López
@truizlop
func cacheUser(byId userId: Int) -> ReaderT<IOPartial<Never>, Env, Bool> {
    return ReaderT<IOPartial<Never>, Env, Env>.ask()
        .map { env in env.api.fetch(byId: userId) }
        .flatMap { io in
            ReaderT { env in io.flatMap { user in env.database.save(user) } }
        }^
}
Finally, you can run this function by passing an Env for production, or another one for testing:
let result = try! cacheUser(byId: 1234)
    .invoke(Env(database: StorageImpl(),
                api: APIImpl()))^
    .unsafePerformIO()
Sébastien Drode
@Dragna
ok I wasn't reasoning about that the right way
I was Thinking of the reader as a Big environment that I had to pass around to my functions
Thank you for your help, I will play with that and see what I can do with it :)
Tomás Ruiz-López
@truizlop
for Writer and State, is quite similar but considering that you can append things to the Env (Writer), or set/get things in the Env (State)
Sébastien Drode
@Dragna
so If I wanted a structure that act like an elm component, State would be the best choice ? something that take an action coming from the UI and run it against the current state to return a new state
Tomás Ruiz-López
@truizlop
most likely
for an example of State, I have a sample in a repo
let me check
Sébastien Drode
@Dragna
and I saw on the last commit that you have a new syntax for monad comprehensions, it seems better than the old one :)
Tomás Ruiz-López
@truizlop
yes, that will come in the next version and I think it will make things a looot easier to write
from that line on, you can see how I use State to track the inputs and outputs to see the Console has the right interactions
the repo might be outdated though, I'll try to update it when I'm back from vacation
Sébastien Drode
@Dragna
ok thank you for your help, I will play with all the informations you gave me :)
Tomás Ruiz-López
@truizlop
You’re welcome! Feel free to ask if you find any issue
Thang Mai
@mxth
 let o1 = Option.some(1)
 let o2 = Option.some(2)
 let o3 = Option.map(o1, o2, { $0 + $1 })
I couldn't make it work, getting Cannot convert value of type 'Option<Int>' to expected argument type 'Kind<ForOption, _>'. I'm new to swift & bow, please help.
Sébastien Drode
@Dragna
maybe the compiler isn't able to infer the type of o3, try like that :
let o1 = Option.some(1)
let o2 = Option.some(2)
let o3 = Option<Int>.map(o1, o2, { $0 + $1 })
Tomás Ruiz-López
@truizlop
Exactly what Sebastien says. You can also do
ForOption.map(o1, o2, +)
Thang Mai
@mxth
It works, thanks guys :)
Sébastien Drode
@Dragna
Hello, I was wondering about something, when should someone need to create a new type of monad ? What I want to do is create a data store (kind of like a redux store) where I can act on state if needed to pass it to subcomponent of a swift UI screen.
I was wondering if this was one of the use cases or not.
Tomás Ruiz-López
@truizlop
I think this is similar to what @bkase was doing with comonadic UIs.
Monads serve to chain dependent computations and need to obey some rules, so whenever you have that need and can fulfill such rules, you can create a new type with a Monad instance
Sébastien Drode
@Dragna
ok, I will look into what @bkase did. Thank you for your answer :)
Tomás Ruiz-López
@truizlop
In any case, post your progress and we can help you
Sébastien Drode
@Dragna
👍
balrajOla
@balrajOla

Hi @truizlop , I was playing around with Bow_Swift and trying to figure out where to use which effect. I have a particular scenario as follows:-

struct User {

    let id: String

    let name: String

}



protocol Storage {

    func get(_ id: String) -> IO<Never, User>

    func save(_ user: User) -> IO<Never, Bool>

}



protocol API {

    func fetch(byId: Int) -> IO<Never, User>

}



struct UserUsecase {

    func getUser(byID id: String) -> What kind of effect can I use here? {

        // Get the data from storage



        // return the cached data



        // fetch the data from API



        // save or update the user data from API response



        // return back the latest user info

    }

}

The above function getUserbyID first gets the user data from cache returns that value and in parallel fetched data from API and updates cache and returns back the updates value.

This particular scenario comes a lot in Mobile client application where we cache and update the data from external source.

So What kind of Effect or return type to use here. As any return type can only return once. The only way I can think of is to use Rx Stream. Do we have any other solution or effect to achieve this in a better way?

Tomás Ruiz-López
@truizlop
Hi @balrajOla, as you mention, functions can only return one value, and IO only produces one value. So, as I see it, you have a couple of options:
  • getUser(byId:) could return a tuple of (cache: IO<Never, User>, network: IO<Never, User>). Remember that both are suspended and nothing is really executed until you call unsafePerforIO or unsafeRunAsync. That way, you can return both.
  • Use ObservableK<Never, User> to stream multiple values. There is a wrapper over RxSwift that makes it compatible with all abstractions provided in Bow. This lets you publish multiple values (cached, network...) from a single call, and then send a termination event.
    There will eventually be a wrapper over Combine once it gets stable that will let you do something like with Observable.
balrajOla
@balrajOla
Thanks @truizlop both the solutions mentioned above do solve the problem. But with the first option responsibility to perform both the IO operations goes to the caller of the function and in the second option this works as stream of data. My thoughts are more inclined towards the second option but do you have any example where would the 1st option have more advantage than the second.
Tomás Ruiz-López
@truizlop
In the first option the caller has fine grained detail about several things: where data is coming from (caller may disregard cache and always prefer fresh data from network), caller knows that there are at most two values coming (whereas in Observable you don't know how many values you can expect), you can type the errors differently (IO<DBError, User>, IO<HTTPError, User>). In this case there is a trade off of the details you want to hide and expose as an API to the caller.
balrajOla
@balrajOla
@truizlop Sure that is a good argument..
balrajOla
@balrajOla
@truizlop Do we have any performance measures.. Like when we build expressions by combining smaller expressions and defer the implementation to the end.. What are the pros and cons in terms of performance/ memory in SWIFT. I know in Kotlin building large expressions had lot of stack build ups hence we use coroutines which is kind of work around to reduce memory buildups from stack to heap. But is there any study in Swift.
Tomás Ruiz-López
@truizlop
We haven't done anything like that yet. I am aware that some implementations in Bow are not stack safe. In terms of performance, it would be nice if someone could come up with some benchmark and get some data.