question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

higher order generics

See original GitHub issue

[@gavinking] We’ve often discussed this feature, type constructor parameterization, or kinds, or whatever you want to call it, which boils down to, by analogy with first-class/higher order functions at the value level, adding:

  • first-class type constructors, and
  • higher-order generic declarations.

That is, I can define generic types and functions which are parameterized not only by types, but also by type constructors. And I can pass a reference to a type constructor as an argument to such a generic type or function.

I now have an implementation of higher order generics in the typeconstructors branch of the typechecker project. An outline of my approach:

  • Type constructors are represented as types within the type system, just like functions are represented as values. This is much less significant than it sounds, since type constructors have no meaningful subtype relationships with other types, but it does let us avoid introducing a whole new level of the type system. In particular, it means that a type parameter that accepts type constructors is still just a type parameter, not some new kind of thing.

  • The syntax @List produces a reference to a type constructor for a generic type declaration. In principle, we could live without the @, but that would make it awfully difficult to distinguish type constructors for ordinary unparameterized types, so I think the @ is a good thing. Reiterating what I said above, @List is considered a type. It’s not, however, meaningful to ask what its values are. Nor does it have any interesting subtype relationships with other types. (Indeed, @List might not even be a subtype of Anything.)

  • A generic declaration may have a parameter that accepts type constructors. The syntax is:

     interface Functor<@Fun> given Fun<Element>
    

    The @ is a visual indication that the parameter expects a type constructor, and the given clause defines the signature required. We could simplify the syntax to interface Functor<Fun<Element>>, but I find this a lot less self-explaining.

  • Within the body of such a parameterized declaration, the type parameter is a parameterized type like any other parameterized type. I can write, for example:

    interface Functor<@Fun> given Fun<Element> {
        shared formal Fun<Out> map<In,Out>(Out apply(In a))
                                          (Fun<In> inFun);
    }
    
  • Finally, I can instantiate the generic declaration by giving it a conforming type constructor:

    object listFunctor satisfies Functor<@List> { ... }
    
  • Now, with just this much machinery I can write code that abstracts over functors, for example:

    Fun<String> toString<@Fun>(Functor<@Fun> functor)
                              (Fun<Object> inFun) 
                    given Fun<Element> 
            => functor.map(Object.string)(inFun);
    

There’s one big limitation remaining in the current implementation: I don’t have type constructor arg inference, so I have to write shit like this:

List<String>(List<Object>) listToString = toString<@List>(listFunctor);

Instead of just:

List<String>(List<Object>) listToString = toString(listFunctor);

I assume that this problem is tractable.

Otherwise, the implementation was surprisingly easy and straightforward though surely there are still a couple of holes.

Now, the only reason I’ve even bothered playing around with this shit at this stage is that we have an actual use for it in the metamodel API. We would like to be able to have typesafe model objects for generic declarations. That is, it would be nice to be able to write:

GenericClass<@List> listGenClass = `@List`;
Class<List<String>> stringListClass = listGenClass(`String`);

And have that be all totally typesafe.

UPDATE: Note, we no longer propose to use this stuff for the metamodel!

What I’m not proposing at all is to go and make Iterable and its subtypes into Functors. It would be possible to do this someday, if we decide it’s truly useful, but I definitely don’t want to do it in Ceylon 1.x.

So, we need to decide if we’re going to make this a part of the language. There are strong arguments on both sides, which I’ll let others advocate, because I simply can’t seem to make up my mind.

[Migrated from ceylon/ceylon-spec#754]

Issue Analytics

  • State:open
  • Created 10 years ago
  • Comments:97 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
CeylonMigrationBotcommented, Nov 14, 2015

[@luolong] Note to self: that soundbite from @RossTate sounds like a true gobbledygook to me 😉

0reactions
gavinkingcommented, Jan 23, 2018

Unrelated: the function reference Map has type Forall K, I. Map<K,I>(K,I), not just Forall K, I. Map<K,I>.

Yes, sorry, of course.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Higher-order type functions in TypeScript? - Stack Overflow
Am I correct assuming that such a type is currently unrepresentable in TS? typescript · generics · nested-generics · open-generics · Share.
Read more >
Generic Higher-Order Functions in TypeScript - Atomic Spin
An example of how to write a generic higher order function in TypeScript using the built in type definitions ReturnType and Parameters.
Read more >
Supporting generic type inference over the other higher-order ...
Currently, TypeScript infers generic types when referring its type immediately. Its behavior is undesirable for using higher-order functions.
Read more >
6 Generics and higher-order functions - Swift in Depth
Understanding higher-order functions. Detecting and creating abstractions. Creating generic functions and methods. Constraining generics with one or more ...
Read more >
Generalizing over Generics in Rust (Part 1) - Rusty Yato
Do you want to use Higher Kinded Types (HKT) in Rust? Are you tired of waiting for GAT? Well, this is the place...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found