idriscombinatorsidris2

Type-safely Implementing an Arbitrary Degree Blackbird Combinator (B-n Combinator)


As the title suggests I'm trying to implement an arbitrary degree blackbird combinator (something I'd always wanted but could never have in Haskell), that is:

bird : (n : Nat) -> (x -> y) -> (a1 -> a2 -> ... -> an -> x) -> a1 -> a2 -> ... -> an -> y
bird k f g x1 ... xk = f (g x1 ... xk)

bird 0 = (&)
bird 1 = (.)
bird 2 = (.:)

But I've been flailing around for an eternity trying to get this to work. This is my first time writing Idris, apologies for the ignorance that ensues:

import Data.Vect

args : Foldable t => Type -> t Type -> Type
args = foldr (\acc, t => acc -> t)

And then two different implementations of my combinator (not tested at the same time!):

bird : {ts : Vect n Type} -> (n : Nat) -> (a -> b) -> args a ts -> args b ts
bird  Z    f     = f
bird (S k) f g x = bird k f (g x)

bird : {ts : Vect n Type} -> (n : Nat) -> (a -> b) -> args a ts -> args b ts
bird k = foldr (.) id $ replicate k (.)

Yeilding this error

Error: While processing right hand side of bird. Can't solve constraint
between: a and foldrImpl (\acc, t => acc -> t) a id ts.

and this one,

Error: While processing right hand side of bird. When unifying:
    ?a -> a
and:
    a
Mismatch between: ?a -> a and a.

respectively. Frankly I think part of the problem might be Idris's odd treatment of composition, for example, (.) . (.) has a vastly different type then it does in Haskell, and frankly it seems to me that Haskell got it right, (.) . (.) : (?b -> ?c) -> (?a -> ?a -> ?b) -> ?a -> ?a -> ?c (Idris) instead of (.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d (Haskell)

Thanks for your help!


Solution

  • That combinator already exists within the Idris2 standard library as chain. It's a bit more annoying to use than your version, as it always requires passing the argument types of the second function, but I guess that's as good as we can get now. By the way, the 0-ary combinator would be $, not &.

    import Data.Fun
    
    double : Int -> Int
    double x = 2 * x
    
    add : Int -> Int -> Int
    add x y = x + y
    
    doubleAdd = chain {ts=[Int,Int]} double add