0

I have the following (simple) data structure:

struct Work<Input, Output> {
    let work: Input -> Output
}

This type represents work which can take an Input and turns in into a desired Output. I am trying to see whether this data structure conforms to some functional concepts like a functor or a monad.

Functor

extension Work {
    func map<MoreOutput>(transform: Output -> MoreOutput) -> Work<Input, MoreOutput> {
        return Work<Input, MoreOutput> {
            return transform(self.work($0))
        }
    }
}

That seems to be correct as far as I am aware. I am able to write a map function which can turn Work<Input, Output> into Work<Input, MoreOutput>

Monad

I have trouble thinking of the definition for a flatMap (or fold) function for Work. The only thing I can come up with is the following:

extension Work {
    func flatMap<MoreOutput>(transform: Work<Output, MoreOutput>) -> Work<Input, MoreOutput> {
        return Work<Input, MoreOutput> { input in
            return transform.work(self.work(input))
        }
    }
}

If you look up the flatMap definition for an Array in swift it looks like this (simplified):

func flatMap(transform: (Element) -> T?) -> [T]

This is a function where its argument is a function which transforms an Element into T and results an Array. I cannot think of a way to abstract this to the Work type.

From another functional book I found a general definition for flatMap as follows (on an object F holding type A):

func flatMap<B>(f: A -> F<B>) -> F<B>

which is a different definition of flatMap than Array seems to implement.

Can someone explain this difference to me? And is it even possible to define a 'correct' flatMap function on Work? Or does Work not satisfy the properties to be a Monad?

** Edit

Thanks phg for so much useful info. I've tried to do the Profunctor definition:

Making Work a Profunctor:

extension Work {
    func diMap<A, B>(fa: A -> Input, fb: Output -> B) -> Work<A, B> {
        return Work<A, B> { arg in
            let input = fa(arg)
            let output = self.work(input)
            return fb(output)
        }
    }
}

Does that look right to you?

4

1 回答 1

1

这个:

func flatMap<B>(f: A -> F<B>) -> F<B>

是你想要flatMap的样子;这是 monad 通常的“绑定”操作。专门针对第二个参数的函数,您会得到所谓的Reader monad

extension Work {
    func flatMap<MoreOutput>(g: Output -> Work<Input, MoreOutput>) -> Work<Input, MoreOutput> {
        // (Reader f) >>= g = Reader $ \x -> runReader (g (f x)) x
        return Work<Input, MoreOutput> {
            g(self.work($0)).work($0)
        }
    }
}

注意:我实际上不会说 Swift,这段代码只是猜测——因此包含了 Haskell 原版。随时在更正的版本中进行编辑。


现在到另一个定义:

func flatMap(transform: (Element) -> T?) -> [T]

我想T?这意味着“可选T”或“可空T”之类的东西。这不是我们通常理解的一元函数,但它是相关的。事实上,关于这种“广义平面地图”的问题一直存在。答案是,如果两个单子是兼容的,即存在一个单子态射 F<A> -> G<A>保持单子结构,那么定义

func wrappedFlatMap<B>(f: A -> F<B>) -> G<B>

这可能正是“选项类型”和列表类型在这里发生的事情,其中​​态射在逻辑上只是

Just x ~> [x]
Nothing ~> []
于 2016-01-15T16:14:09.167 回答