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
Tomás Ruiz-López
@truizlop
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
Tomás Ruiz-López
@truizlop
If you give it a try and need help, just let us know!
Devesh Shetty
@devesh-shetty
Awesome, thanks!
Georg Dresler
@ge-org
Bow Arch looks really great. I've been playing around with it a bit and have a question
Is it possible to access the current state in the dispatcher? So I could perform an action depending on the current state. Or would I need to pass all required info using the input?
Tomás Ruiz-López
@truizlop
Hi Georg! Thanks for your kind words. When you are returning a State, you get access to the “current” state, but at that point you won’t be able to perform side effects. If you need to perform a side effect based on something from the current state, then yes, you need to pass that info as part of the data attached to an input.
Georg Dresler
@ge-org
Thanks for your answer Tomás
Tomás Ruiz-López
@truizlop
You’re welcome! Let me know if you have any additional questions, and keep us updated on your progress! I’m looking forward to seeing your proyect.
Georg Dresler
@ge-org
Back with another question 🙂 Is there a way to 'inject' an input into a dispatcher 'from the outside'? What I'm thinking about is having an observer (database, geo location,...) that needs to update the state of a component when new data becomes available.
Tomás Ruiz-López
@truizlop
This is an interesting question. Bow still does not have anything like streams, which will model something like this in a very nice way. We have bindings with Rx and it won't be hard to make an adaptation to make Combine work with Bow to do something like this. Just using what we have right now, you would have to start observing when the view appears, and use the handle: (Input) -> Void function to notify actions to Bow Arch whenever an event is triggered in the observer. If you have some sample code we can discuss around a concrete example.
Maureen Elsberry
@MaureenElsberry
Hey all, we've just released Bow Lite! A more beginner-friendly version of Bow: https://www.47deg.com/blog/bow-lite-0-1/
This library will be featured prominently in @truizlop's upcoming Fundamentals of Functional Programming in Swift course happening this September 1st-2nd with the 47 Degrees Academy. Details can be found here: https://www.47deg.com/academy/2020-09-01-Functional-Programming-in-Swift-with-Bow-course/
kyrill
@cyrillyun

hey channel,

I recently started looking at Bow and it looks absolutely awesome. Kudos to the maintainers for putting it together. I have a question about pattern matching data types in Bow. I'd like to apologize in advance for it, as the question is very basic.

let's say I have a simple Option<String>. How do I pattern match it? The following code:

let myObject: Option<String> = .some("42")

switch myObject {
    case let .some(c):
         // ...
}

produces the following compilation error Pattern variable binding cannot appear in an expression

Is there a way to use pattern matching with Options and other data types in Bow?

Tomás Ruiz-López
@truizlop

Hi Kyrill! Thanks for your nice words, it helps us be motivated to continue moving this project forward!

Regarding your question, it is not possible to use pattern matching on the types provided in Bow. The reason is the emulation of Higher Kinded Types we do; we need our types to be classes in order to be able to use them as HKTs. Only enums can be used in pattern matching. Nevertheless, all types provide a method fold, where you can pass closures to handle each of the cases:

let myObject: Option<String> = .some("42")

myObject.fold(
  { /* Handle none case */ },
  { c in /* Handle some case */ }
)

This is possible with any other data type in Bow, like Either:

let myObject: Either<Int, String> = .right("42")

myObject.fold(
  { left in /* Handle left case */ },
  { right in /* Handle right case */ }
)

You can read more about HKTs and how they are emulated in Bow here, and feel free to ask any questions you may have!

If you decide to use our recently released library Bow Lite, we got rid of the emulation of HKTs. With this, we lost a lot of abstraction power, but one of the things we got back is pattern matching. Therefore, if you use Bow Lite, you can still use fold on all types, but you can also pattern match:

import BowLite

let myObject: Either<Int, String> = .right("42")

switch myObject {
  case let .left(l): /* Handle left case */
  case let .right(r): /* Handle right case */
}

I hope this clarifies your question!

Dmitry Sabanin
@dsabanin
@truizlop thank you and other contributors for the great libraries! You can tell how much work went into both Bow and Bow Lite. I have a question regarding future direction. Do you feel like bow-lite is the path forward for most projects? Or do you still think HKT emulation in swift is worth the cost, both in syntax complexity and performance? Just to be clear, I've seriously tried both by migrating my project to both, and then chose to stay with Bow for the flexibility. But I was curious about your thoughts on this.
While we're at it, was there any attempt to measure the impact on the performance of heavy Bow usage in a project? That could be interesting. I would assume heavy class usage is going to have a toll. I have not yet done any formal benchmarking myself, but I'm planning on it
Tomás Ruiz-López
@truizlop

Hi @dsabanin! Thanks for your kind words! We made Bow Lite in order to ease the learning process for newcomers to FP. It takes a while until you fully grasp HKTs, and since they are not native to the language (yet), we have observed many people have issues understanding and using the library. It is also a big commitment to add HKT emulation into your project, especially if you are working with others and they are not very familiar with FP.

Having said that, of course, Bow is much more versatile thanks to HKTs. You can see how much we could save being able to use more powerful abstractions by checking the implementations we had to make in Bow Lite, where there is plenty of duplication that cannot be abstracted out. We were even able to implement Bow Arch on top of the HKT emulation, which lets us replace a few types and get a whole new architecture with different properties. My recommendation is that, if you and your team are familiar with FP and HKTs, go ahead with Bow, and only choose Bow Lite if you want a lightweight library to practice FP without the heavy features.

Regarding benchmarking, I unfortunately haven't been able to do anything about it, but it is definitely something interesting to measure. I'd be interested in measuring which parts of the library are less performant and improving them. Hacktoberfest is coming, so if you are interested in doing this type of project, you are really welcome!

Dmitry Sabanin
@dsabanin
@truizlop awesome, thanks for the response! I'm thinking about doing some benchmarks and publishing the results, just need to find the time to do it right
and I'd definitely love to contribute
Tomás Ruiz-López
@truizlop
Let me know if I can help you get started!
Dmitry Sabanin
@dsabanin
@truizlop when you get a chance, let me know what you think about this one: bow-swift/bow#610
Tomás Ruiz-López
@truizlop
cool! didn't think of adding this conformance before, but definitely improves the ergonomics of using Option
Dmitry Sabanin
@dsabanin
awesome, I love your project and I'm happy to finally being able to contribute something back. Swift is a top-notch language, but after Scala, Haskell, Erlang, OCaml, it's really hard to live without certain things. thanks again
Tomás Ruiz-López
@truizlop
Thank you for your contribution!
Ezio Auditore da Firenze
@quickfingers
Hi everyone, I'm a bit new to functional and looking for a project example using bow swift.
Tomás Ruiz-López
@truizlop
Hi! The most complete example we have right now is the nef Playgrounds app for iPad, that showcases the usage of Bow, Bow Arch and Bow OpenAPI: https://github.com/bow-swift/nef-editor-client
You can find this app published in the App Store
Other than that, refer to the docs for examples on how to use it: https://bow-swift.io/next/docs/
There, you can find other repos using Bow
Cameron de Bruyn
@africanSuperStar
Wow that repo is great. I’ve been trying build a Autoencoder/Parser with Bow and I’m struggling a bit. But that repo will help a lot. Something like Aeson, Argo or TryParsec. Thanks.
Tomás Ruiz-López
@truizlop
If you have some concrete examples that you need help with, just let me know. I think a Parsing library will be a great addition to the Bow ecosystem!