by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Oct 09 2019 10:41
    iboss-ptk synchronize #460
  • Oct 09 2019 10:21
    iboss-ptk edited #460
  • Oct 09 2019 10:21
    iboss-ptk edited #460
  • Oct 09 2019 10:20
    iboss-ptk opened #460
  • Oct 09 2019 09:58

    truizlop on utilities

    (compare)

  • Oct 09 2019 09:58

    truizlop on master

    Miscellaneous utilities (#459) … (compare)

  • Oct 09 2019 09:58
    truizlop closed #459
  • Oct 09 2019 07:53
    truizlop review_requested #459
  • Oct 09 2019 07:53
    truizlop review_requested #459
  • Oct 09 2019 07:53
    truizlop opened #459
  • Oct 09 2019 07:53

    truizlop on utilities

    Add docs (compare)

  • Oct 09 2019 07:52

    truizlop on utilities

    Fix wrong documentation Add instances of Semigroup and … Add utility to Kleisli and 2 more (compare)

  • Oct 09 2019 07:03
    truizlop commented #448
  • Oct 09 2019 01:34
    iboss-ptk commented #448
  • Oct 09 2019 01:28
    iboss-ptk commented #448
  • Oct 08 2019 10:42

    miguelangel-dev on calvellido-patch-1

    (compare)

  • Oct 08 2019 10:42

    miguelangel-dev on master

    Fix bad space char at docs (#45… (compare)

  • Oct 08 2019 10:42
    miguelangel-dev closed #458
  • Oct 08 2019 10:42
    miguelangel-dev commented #458
  • Oct 08 2019 10:39
    calvellido edited #458
Miguel Ángel Díaz
@miguelangel-dev
:)
Marcelo Jasek
@marcjal
Hi 👋
Where I can find a project using this library?
Miguel Ángel Díaz
@miguelangel-dev

Hi Marcelo, it depends on kind the project you are looking for. For now, as OSS, we are working in a refactor in another library from bash to FP in pure Swift. It is nef: https://github.com/bow-swift/nef - and you can find the project using an architecture FP thanks to Bow: https://github.com/bow-swift/nef/tree/develop/project

It is nice because in the same project you can find an example how to make a functional-library (API) and how to use it (CLI) in pure Swift with Bow

if you open the nef tree-project you can find 4 modules: UI, Component, Core and Tests.
UI: examples Bow in CLI
Component: example of FP-architecture with Bow using Bow, BowEffects, BowOptics
Core: business logic and how to wrap into a FP architecture
Tomás Ruiz-López
@truizlop
Also, our project Bow OpenAPI is built using Bow and Bow Effects: https://github.com/bow-swift/bow-openapi
We are working towards providing more examples for macOS and iOS apps using FP and Bow. If you get something, let us know and we can link it as a reference in our documentation
Devesh Shetty
@devesh-shetty
Hi team,
Could I work on this? bow-swift/bow#286
Tomás Ruiz-López
@truizlop
Sure! I can assign it to you.
Devesh Shetty
@devesh-shetty
Awesome, thanks!
Sébastien Drode
@Dragna

hi everyone !

what would be the right way to execute an IO on background queue and get back to main queue to use the result ?

I tried

effect.attempt(on: .global(qos: .background)).unsafeRunAsync(on: .main,
                    { resultAction in
                        print(resultAction)
                        self.send(resultAction.rightValue) //Should never error out
                    })

but it seems to block the UIThread

Sébastien Drode
@Dragna
I tried with comprehensions too, but I think I'm missing something 🤔
            let action = IO<Never, Action>.var()
            let sendEffect = binding(
                continueOn(.global(qos: .userInitiated)),
                action <- effect,
                |<-ConsoleIO.print("action is \(action.get)"),
                continueOn(.main),
                |<-IO.invoke { self.send(action.get) },
                yield: ()
            )^
            try! sendEffect.unsafeRunSync()
Tomás Ruiz-López
@truizlop
The first code blocks because attempt is synchronous. Even though you are passing a different queue to run it, it will block the current queue until it finishes, and then do the unsafeRunAsync. The second one should work fine if you call sendEffect.unsafeRunAsync. It is the same reason: it blocks because it is synchronous.
Sébastien Drode
@Dragna
hmm even with unsafeRunAsync it's acting this way :/
public func send(_ action: Action) {
        let effects = self.reducer(&self.value, action)
        print(effects)
        effects.forEach { effect in
            let action = IO<Never, Action>.var()
            let sendEffect = binding(
                continueOn(.global(qos: .userInitiated)),
                action <- effect,
                |<-ConsoleIO.print("action is \(action.get)"),
                continueOn(.main),
                |<-IO.invoke { self.send(action.get) },
                yield: ()
            )^
            sendEffect.unsafeRunAsync({ _ in

            })
        }
    }
the send method is the method I use to send action to a redux Store, I want to execute the effects gotten from the reducer and execute the on background, get their result back and pass them to the .send method again
the problem is maybe elsewhere, I'll look
Sébastien Drode
@Dragna
if I use simply unsafeRunAsync(on:, callback: ) with a background queue, it works fine, but I get an alert from swiftui telling that I'm publishing changes from background ^^ which is true in this case
effect.unsafeRunAsync(on: .global(qos: .background),
            { resultAction in
                print(resultAction)
                self.send(resultAction.rightValue) //Should never error out
            })
Tomás Ruiz-López
@truizlop
If you can get a minimal project so that we can reproduce it, it'd be great
Sébastien Drode
@Dragna
yes I'll do that
Sébastien Drode
@Dragna
I gave something that works here with a DispatchQueue.main.async
but when I'm using .binding, the UI is blocked when I make the network call even though the log say that it's running on the right queue
(the network call is on the second tab)
Tomás Ruiz-López
@truizlop
Let me find some time to look at it, I'll come back to you
Sébastien Drode
@Dragna
No problème, thank you for your help, I’ll keep looking at it to find the problem
Tomás Ruiz-López
@truizlop
@Dragna I think what you may be looking for is something like this:
let single: IO<Never, [Void]> = effects.traverse { effect in
                let action = IO<Never, Action>.var()
                return binding(
                    continueOn(.global(qos: .userInitiated)),
                    action <- effect,
                    continueOn(.main),
                    |<-IO.invoke { self.send(action.get) },
                    yield: ())
            }^
            single.unsafeRunAsync(on: .global(qos: .userInitiated)) { _ in }
traverse will get you a single IO describing the effects of your array of IO, and collecting all results in an array. You can also use parTraverse if you'd like the execution of the effects to be run in parallel.
Miguel Ángel Díaz
@miguelangel-dev
Agree! On another hand, just for readability you can use the data type UIO<A>, it is a typealias to IO<Never, A>
Tomás Ruiz-López
@truizlop
With that, you don't need to wrap the body of send in a DispatchQueue.main.async, as the continueOn(.main) will do that for you.
so your send could be something like:
public func send(_ action: Action) {
        let effects = self.reducer(&self.value, action)
        let single: UIO<[Void]> = effects.traverse { effect in
            let action = UIO<Action>.var()
            return binding(
                continueOn(.global(qos: .userInitiated)),
                action <- effect,
                continueOn(.main),
                |<-UIO.invoke { self.send(action.get) },
                yield: ())
            }^
        single.unsafeRunAsync(on: .global(qos: .userInitiated)) { _ in }
    }
Sébastien Drode
@Dragna

oh great, I will try that, thank you for your help 🙂.
an other question on the API, wouldn't it be great to have this possibility :

effect.execute(on: .global(qos: .background)) // execute the IO on the background thread
            .unsafeRunAsync(on: .main,  // run the callback execution on the main thread
                { resultAction in
                    print(resultAction)
                    self.send(resultAction.rightValue) //Should never error out
                })

I find it hard to resort to Monad comprehension for a simple case like this. And the empty unsafeRunAsync is bothering me :). But there is maybe something like that already and I may have missed it.

Sébastien Drode
@Dragna
and so that I better understand what I did wrong, is the fact that I wasn't running the unsafeRunAsync closure on a background queue ?
Tomás Ruiz-López
@truizlop
Let me go one by one on your questions:
  • Once you call unsafeRunSync or unsafeRunAsync, you shouldn't make any other operation. We could add to the API a default empty closure, so that you don't have to pass it yourself.
  • You don't need to resort to monad comprehensions to switch the queue. IO also has a method continueOn that you can invoke to switch the queue of whatever comes next. In any case, you can still do what you are suggesting by calling attempt (your execute in the example above) and then the unsafeRun.
  • The problem was that you were wrapping everything on a DispatchQueue.main.async and then launching the unsafeRunAsync also in the main queue, and causing a deadlock.
Sébastien Drode
@Dragna

I added the DispatchQueue.main.async just before pushing the code, because it was working as I wanted to with it. I didn't have that when I was asking initially.
Thank you for your help on understanding that, it is really nice of you. So don't hesitate to tell me if it's bothering you, I don't want to take too much of your time. :)

so by doing something like that :

    public func send(_ action: Action) {
            print("send method called on \(DispatchQueue.currentLabel)")
            let effects = self.reducer(&self.value, action)
            print(effects)
            effects.forEach { effect in
                effect.attempt(on: .global(qos: .userInitiated))
                    .unsafeRunAsync(on: .main,
                                    { resultAction in
                                        print("Resulting action : \(resultAction) on \(DispatchQueue.currentLabel)")
                                        self.send(resultAction.rightValue) //Should never error out
                                    })
            }
    }

(I don't have the Traverse, but I want to understand with my initial code 🙂 )
I should have the same result as with your propose solution ? But it's not the case, I still have the deadlock . So what would be the right way to do it this way ?

Tomás Ruiz-López
@truizlop
Don't worry, we are here to help!
The issue there is that attempt is synchronous and you are invoking it from the main queue, which is later needed to run the async closure, and causes a deadlock
Sébastien Drode
@Dragna
oh yes ! you told me yesterday that attempt was synchronous
Sébastien Drode
@Dragna

Ok I think I put the finger on what is bothering me, the unsafeRunAsync run the whole computation of the IO, not just the ending callback on the thread that we pass, meaning that it will wait (because it wrap a unsafeRunSync call) for my IO computation to be executed before executing the callback on the given thread

Am I right with that ?

I assumed the IO would be executed asynchronously on any previously given Queue, then the callback would be called when the computation finished and this same callback would be called on the queue given through the .unsafeRunAsync method

Sébastien Drode
@Dragna
I think I don't make sense ^^ what I was assuming is that it worked like that :
extension IO {
    public func attemptAsync(on queue: DispatchQueue = .main) -> IO<E, A> {
        queue.shift().followedBy(self)^
    }

    public func myUnsafeRunAsync(on queue: DispatchQueue = .main,  _ callback: @escaping Callback<E, A>) {
        self.unsafeRunAsync(on: .global(qos: .background)) { (result) in   // I don't know on which queue it should run by default, it's only to show what I was assuming
            queue.async {
                callback(result)
            }
        }
    }
}

// using this extension I can now define the send method like that : 

    public func send(_ action: Action) {
            print("send method called on \(DispatchQueue.currentLabel)")
            let effects = self.reducer(&self.value, action)
            print(effects)
            effects.forEach { effect in
                effect
                    .attemptAsync(on: .global(qos: .userInitiated))
                    .myUnsafeRunAsync({ resultAction in
                                        print("Resulting action : \(resultAction) on \(DispatchQueue.currentLabel)")
                                        self.send(resultAction.rightValue) //Should never error out
                                    })

            }
    }
Sébastien Drode
@Dragna
I saw the .unsafeRunAsync as the finality of my IO, where I collect my data kind of like the .sinkin combine. but maybe it's not the right way of seeing things and I should see the final computation (getting the action back and feeding it to the .sendmethod) as part of my whole IO computation (the solution you gave earlier)
Georg Dresler
@ge-org

Hi,

I have a question regarding monad comprehension. Is it possible to run an IO using binding without waiting for the result of the execution of the IO?
My use case is, I want to trigger loading data from some source (API, DB,..). Once the data is loaded it will be made available using a Publisher. So I want to have a program that starts loading the data and then immediately returns without waiting for the loading to complete.

import Foundation
import Bow
import BowEffects
import Combine

func myProgram() -> IO<Never, ViewModel> {
    let publisher = CurrentValueSubject<Data, Never>(Data())
    return binding(
        // is it possible to run this IO without waiting for it to return?
        |<-loadData(publisher: publisher),
        yield: ViewModel(data: publisher.eraseToAnyPublisher())
    )^
}

func loadData(publisher: CurrentValueSubject<Data, Never>) -> UIO<Void> {
    UIO.invoke {
        // asume this takes a couple of seconds
        let data = Api.getMyData()
        publisher.send(data)
    }
}

// stubs
struct Data {}
struct Api {
    static func getMyData() -> Data {
        Data()
    }
}
struct ViewModel {
    let data: AnyPublisher<Data, Never>
}
Tomás Ruiz-López
@truizlop
Hi Georg. Let me clarify this a little bit. When you do a binding, IO is not run; it is just a way of composing values, like you'd do using flatMap. IO will be run when you invoke any of the unsafeRun methods, so depending on what you choose, that operation will be synchronous or asynchronous. That is, if you invoke myProgram().unsafeRunAsync(on: queue) { ... } and pass the appropriate queue, it will return immediately while doing the operation on the queue that you passed. Be aware that, if you don't send a queue, the main one will be used, and it could lead to a deadlock.
Georg Dresler
@ge-org
Thanks for the explanation @truizlop 🙏
I understand that binding() is syntactic sugar for a bunch of flatMap()s and the resulting IO needs to be executed using one of the unsafeRun functions.
So I guess, if I want to 'fire and forget' some side effect I just need to call unsafeRunAsync() on it. And if I want to do it within a binding I need to wrap it in an IO. E.g. UIO.invoke { loadData.unsafeRunAsync() }.
In Arrow there is syntactic sugar for this: !effect { }. That's why I was under the impression the same thing could be achieved in Bow using less code.
Robert Collins
@robbiemu
Robert Collins
@robbiemu
nevermind, I found the problem :) the project must be a playground project generated by nef
Devesh Shetty
@devesh-shetty
Hi team,
Congrats on the launch of Bow Arch 0.1.0! Is there any sample using Bow Arch?
Tomás Ruiz-López
@truizlop
Thanks Devesh! The docs include some samples, and here you can find a simple Tic Tac Toe https://github.com/truizlop/TicTacToeArch
We will be publishing more complete examples shortly, so much looking forward to showing what we’ve done!
Devesh Shetty
@devesh-shetty
Thanks @truizlop