5

The wiki says:

monadic functions (i.e. functions that use values from the monad as their arguments or return value).

My understanding is a function takes or returns a monad is monadic function, but it seems it has more strict definition when I came across this blog

The author said:

A monadic function is a function that produces a monadic value. (Note that we said nothing about its input type)

and

Functions of the form f :: a -> m b, where a is the type of the inner value of the monad. (Call these classic monadic functions)

Functions of the form f :: anything -> m b, where the input of the function really doesn't matter. (Call these loose monadic functions)

It seems the definition is pretty strict and formal, but I can't find anywhere says anything about classic monadic functions, loose monadic functions.

So what precisely is a monadic function?

AndrewC
  • 32,300
  • 7
  • 79
  • 115
Sawyer
  • 15,581
  • 27
  • 88
  • 124
  • 2
    The main reason you can't find those terms anywhere else is indicated at the top of that article, which says: _It contains my personal and intuitive way of thinking of monadic functions_ – AndrewC Feb 18 '14 at 15:16
  • So my question still is "what's a monadic function"? `a -> m b` is monadic function, what about `m a -> b`? – Sawyer Feb 18 '14 at 15:22
  • 8
    "Monadic function" isn't a strictly defined term. Monad is strictly defined, monadic function isn't. If you mean `a -> m b` you could say Kleisli arrow or monadic function. You could call `ma -> m b -> m c` a monadic function. It depends on the context, and I'm afraid there's no definitive answer. – AndrewC Feb 18 '14 at 15:30
  • 4
    Maybe we could help you better if we only knew what relevance this has for you. Mind you, we're talking about definitions. Everybody is free to define the term he uses as he sees fit. To be sure, once a certain term is established, it is unwise to stick with ones own different definition. But, as you say, you don't find the terms widely used, so either take over the definition from the blog you cited, or come up with an own. – Ingo Feb 18 '14 at 15:43
  • 4
    This distinction between "classic" monadic functions and "loose" monadic functions is not well-defined. There's no such thing as "the inner value of the monad". I wouldn't take this blog post too seriously. We all like to share the journey we took to discover new concepts, but that doesn't mean our understanding of the journey will be helpful to others, or contain well-defined standard concepts. – Tom Ellis Feb 18 '14 at 16:33
  • 4
    I would call `a -> m b` a Kleisli arrow and `m a -> b` a Cokleisli arrow. The former is generally connected to Monads and the latter is generally more connected to Comonads. This way we can avoid the vague terminology that can cause some confusion. Note how the Kleisli arrow fits into the `>>=` (bind) Monad method and how the Cokleisli arrow fits into the `extend` (aka `<<=`) Comonad method. – David Young Feb 18 '14 at 18:45

3 Answers3

4

A short and (I hope) simple answer to this is that a monadic function is one whose return data signature/type is the same as its input data signature/type.

Imagine the function for the plus operator:

def plus(a,b):
    return a+b

This isn't monadic since it takes a tuple, but returns a single value.

But rewritten as:

def monad_plus(a,b):
    if b is None:
        return (a, None)
    else:
        return (a+b, None)

Then the result of the function could be fed into another function call without any changes.

You might then create a series of functions that both accepted the (a,b) input and returned the same (a,b) outputs,

def monad_mult(a,b):
    if b is None:
        return (a, None)
    else:
        return (a*b, None)

def monad_div(a,b):
    if b is None:
        return (a, None)
    else:
        return (a/b, None)

I believe that practically, you should handle the degenerate cases where a ○ None should perform no change to a, which is why I've explicitly added those if is None tests, but would welcome advice on this.

The practical end result should be that you're able to string a whole collection of these calls together, and submit their execution to be performed in a variety of ways - serial, distributed etc, in batches or in sequence against a dataset, and remain confident of always returning the same result.

Thomas Kimber
  • 10,601
  • 3
  • 25
  • 42
1

I understand you reference this article in Wikipedia

In your context, "monadic functions" designates functions that you would compose to write bigger ones, taking advantage of the implicit context handling done by (>>=) and return.

Example : suppose you have a Map of Maps to represent the output values of 4 combinations "aa" -> 1, "ab" -> 2, "bc" -> 3, "bd" -> 4

import Data.Map (Map, lookup, fromList)

type Map1 = Map Char Int
v1 = fromList [('a',1),('b',2)]    :: Map1
v2 = fromList [('c',3),('d',4)]    :: Map1

type Map2 = Map Char Map1
myMap = fromList [('a',v1),('b',v2)]   :: Map2

Your best friend Hoogle says that lookup :: Ord k => k -> Map k a -> Maybe a

Here lookup is the so-called "monadic function" that must be composed (here with itself) to give a function of type :: Char -> Char -> Map2 -> Maybe Int

composedLookup a b m = do     
        v  <- lookup a m
        v' <- lookup b v
        return v'

or

composedLookup' a b m = lookup a m >>= (lookup b)

EDIT : And functions of type m a -> b would be called comonadic in the context of m being a comonad. I found this great SO answer about algebra/coalgebra quite enlightening because it ultimately explains monads and comonads in terms of types and applications.

Hope this helps

Community
  • 1
  • 1
Titou
  • 968
  • 10
  • 16
1

Let's examine the possibilities of having a monad in an input or output of a function.

  1. Monad in one of the input arguments: f :: m a -> X. Because for any monad we have return :: a -> m a, we can compose it with f and get

    f . return :: a -> X
    
  2. Pure output: f :: X -> b. By composing it with return from the other side we get

    return . f :: X -> m b
    

So we can always convert a pure output into monadic one and a monadic input into a pure one. And any function that has pure or monadic inputs/output can be always converted to the form

    f :: a1 -> ... -> an -> m b

However, the reverse isn't possible (in general).

I'd say that there is no wide use of terms 'classic' and 'loose' monadic function.

Petr
  • 62,528
  • 13
  • 153
  • 317