Occasionally, absolutely crazy ideas crop up into my noggin. Recently, I've had two take up residence almost simultaneously, both related to pynads.

## Haskell Type Signatures

Since Pynads is, nominally, a learning exercise for me to understand
some concepts in functional programming -- specifically in terms of
Haskell -- a little more deeply. I found myself wanting to using Haskell
type signatures with some of my functions. The reason why is because I
*like* Haskell's type signatures. They stay out of the way of my actual
function signatures.

Here's the current way Python 3 annotates functions:

def my_func(a: int, b: str = 'hello') -> tuple: return (a, b) my_func(1, 'wut')

(1, 'wut')

That's *so much line noise*. Like what. Look at that default assignment.
Like, I get why the annotations are inlined with the signature. But
they're just ugly. Meanwhile, here's a similar Haskell function with the
type signature:

myFunc :: Int -> String -> (Int, String) myFunc a b = (a, b)

That type signature is both helpful AND out of the way. However, there's one really nice thing that Python does with these annotations:

my_func.__annotations__

{'a': int, 'b': str, 'return': tuple}

Nice. We retain that information in a dictionary out of the way. What if we could combine these two things?

from pynads.utils.decorators import annotate @annotate(type="Int -> String -> (Int, String)") def my_func(a, b='hello'): return (a, b)

I like this. It stays out of the way, it uses a decorator (never enough
decorators). Let's checkout the `__annotations__` dict:

my_func.__annotations__

{}

Uh....Well, it didn't fill anything in. What did it do? Well, it attaches the signature to the docstring...

print(my_func.__doc__)

my_func :: Int -> String -> (Int, String)

That's...nice. I'm actually perfectly content with this solution currently. But wouldn't it be cool? (this is a phrase that only preceeds michief and trouble). Wouldn't it be cool if that Haskell type was somehow transformed into a Python annotations dictionary and on the other end we'd be able to inspect the annotation and get this:

{'a': 'Int', 'b': 'String', 'returns': '(Int, String)' }

{'a': 'Int', 'b': 'String', 'returns': '(Int, String)'}

However, this is complicated because what if we had a higher order function? The Haskell type signature looks like this:

map :: (a -> b) -> [a] -> [b]

"Take a function of type a to type b, a list of as and return a list of bs." Simple. How is this parsed? What if we put type class restrictions on the type:

(Monad m) => m a -> (a -> b) -> m b

Where m is an instance of Monad, take a `m a` and a function of type a
to b and return a `m b`.

What if we have multiple typeclass restrictions:

(Monad m, Monoid s) => m s -> m s -> (s -> s -> m s) -> m s

Maybe we're lifting `mappend` into a monad? Let's also pretend that
this isn't a bad way of doing it as well. How do we parse this?

What about "existentially types", aka. `forall a b.` (which is
something I've not used, nor understand, but apparently it's useful
because reasons).

Of course, there are cons with this:

- Haskell type signatures are too complex to be matched with regular
expressions. How do you write a regex for
`forall a b. ((a, b) -> (Char, Bool))`? - Parsers can be slow. They
*can*be fast. But since this is a library built on understand functional things like applicatives and monads, of course I'd use an applicative/monadic combinatorial parser, which in Python*will*be slow. - So many competing Python typing libraries.
`mypy`seems to have gotten the BDFL nod and in fact, seems to be on track for inclusion to Python 3.5. Is it worth taking a second step and translating the parsed type signature to a type system? `__annotations__`are use for things that aren't type annotations. Is this good? Is this bad? I don't know. Well, I think they*should*be for type signatures, but there's some cool stuff that is done with them.- What about typeclass restrictions? Do we create a special entry? How do we handle collisions? Etc.

Things to think about.

## My Broken Do Notation

I implemented a rudimentary do-notation esque
syntax
sourced from Peter Thatcher's post on monads in
Python.
Here's the thing, it works...except with the `List` monad, because we
need to repeatedly call the bound function and then flatten the list
out. Except with this implementation, it hits `mreturn` and the whole
thing blows up. Wah wah wah. But I like this style:

from pynads.do import do, mreturn from pynads import List @do(monad=List) def chessboard(ranks, files): r = yield List(*ranks) f = yield List(*files) mreturn((r,f)) #chessboard('abcdefgh', range(1,9))

Oops. But this works:

def chessboard(ranks, files): return List(*ranks) >> (lambda r: List(*files) >> (lambda f: List.unit((r,f)) )) chessboard('abcdefgh', range(1,9))

List(('a', 1), ('a', 2), ('a', 3), ('a', 4), ('a', 5), '...54 more...', ('h', 4), ('h', 5), ('h', 6), ('h', 7), ('h', 8))

But compare to Haskell:

ranks = ['a'..'h'] files = [1..8] chessboard :: [(Char, Integer)] chessboard = do r <- ranks f <- files return (r,f)

It just works. Obviously, something in Pynads is implemented poorly. Wah
wah wah. But what if `do` did something different? What if it took a
look at that function and said, "No no no, this is *all* wrong...let
me...let me just rewrite it for you." And instead of actually using:

def chessboard(ranks, files): r = yield List(*ranks) f = yield List(*files) mreturn((r,f))

We actually end up using:

def chesboard(ranks, files): return List(*ranks) >> (lambda r: List(*files) >> (lambda f: List.unit((r,f)) ))

And just rewrite the function with AST and compile it and use the new function instead. I mean if Lisp can be implemented in Python by parsing Lisp into Python AST, surely it's (relatively, being the keyword) simple to take a function with a bunch of yields and rewrite it into a function with a bunch of lambdas.

Besides, wouldn't it be cool? ;)

## Thoughts

Of course both of these are nuclear options. Well, the parser less so than rewriting AST. But, I know if I become a professional developer, at some point I'll need to have a passable understanding of parsers and ASTs (they're not even that unrelated) so instead of breaking something someone's using, why not break something I'll be the only one cursing?

I spilled my brains, spill some of yours.