# Type Checker: Inference for polymorphic keyword functions not supported

**URL:** <https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145>\
**Category:** Questions & Answers\
**Tags:** question, typed-racket\
**Created:** [November 16, 2021, 9:53am UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145 "2021-11-16T09:53:34Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [November 16, 2021, 9:53am UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/1 "2021-11-16T09:53:34Z")

</div>

Hi,

I want to do a union of two hash tables mapping symbols to numbers, summing up the values for duplicate keys. Here's what I write:

```auto
#lang typed/racket

(require racket/hash)

(define h1 : (Immutable-HashTable Symbol Number) #hash((a . 1)))
(define h2 : (Immutable-HashTable Symbol Number) #hash((a . 2)))

(: combine-plus (-> Number Number Number))
(define (combine-plus a b) (+ a b))

(hash-union #:combine combine-plus h1 h2)

```

This gives the error "Type Checker: Inference for polymorphic keyword functions not supported."

When I use `inst` like so:

```auto
((inst hash-union Symbol Number) #:combine combine-plus h1 h2)

```

I get "Type Checker: Unexpected keyword argument #:combine".

What should I do to get this example to typecheck?

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [November 17, 2021, 4:04pm UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/2 "2021-11-17T16:04:47Z")

</div>

It appears that the current type provided by Typed Racket for hash-union does not include support for the #:combine feature:

```
> (:print-type hash-union)
(All (a b)
  (-> (Immutable-HashTable a b) (HashTable a b) * (Immutable-HashTable a b)))
>

```

I don't see any TR issues that reference hash-union, so it's possible that this is something that could easily be added, although my intuition is extraordinarily weak here.

There are also many workarounds... hm. Well, I tried one I hoped would work, and got a really fun error.

Specifically, I tried this:

```
#lang typed/racket

(require/typed
 racket/hash
 [hash-union
  (All (a b) (-> (Immutable-HashTable a b)
                 #:combine (b b -> b)
                 (HashTable a b) *
                 (Immutable-HashTable a b)))]
 )
 

(define h1 : (Immutable-HashTable Symbol Number) #hash((a . 1)))
(define h2 : (Immutable-HashTable Symbol Number) #hash((a . 2)))

(: combine-plus (-> Number Number Number))
(define (combine-plus a b) (+ a b))

((inst hash-union Symbol Number) #:combine combine-plus h1 h2)

```

And it type-checks! Yay! ... But when I run it, I get this error:

```
hash/c: contract violation
  expected: chaperone-contract?
  given: a7
> 

```

@samth would certainly be able to answer this question.

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [November 17, 2021, 11:12pm UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/3 "2021-11-17T23:12:33Z")

</div>

Thank you @jbclements for your reply. I did check the type of `hash-union` after I started the discussion (altough I forgot about `:print-type`, that's neat!) and I saw indeed that the type did not include any one of the optional keyword arguments.

Thank you also to have tried `require/typed`: I get exactly the same error, even the `7` in `a7`.

I tried another workaround with an untyped submodule converting the keyword argument into a normal argument:

```
#lang typed/racket

(module hash-union-submod racket/base
  (require racket/hash)

  (provide hash-union2-combine)
  (define (hash-union2-combine ht1 ht0 combine-fun)
    (hash-union ht1 ht0 #:combine combine-fun)))

(require/typed 'hash-union-submod
  [hash-union2-combine (All (a b) (-> (Immutable-HashTable a b)
                                      (Immutable-HashTable a b)
                                      (-> b b b)
                                      (Immutable-HashTable a b)))])

(define h1 : (Immutable-HashTable Symbol Number) #hash((a . 1)))
(define h2 : (Immutable-HashTable Symbol Number) #hash((a . 2)))
h1
h2

(: combine-plus (-> Number Number Number))
(define (combine-plus a b) (+ a b))

(hash-union2-combine h1 h2 combine-plus)

```

I really expected _this_ to work, but guess what:

```
; hash/c: contract violation
; expected: chaperone-contract?
; given: a4

```

Well, the good news is that this seems to be the same kind of error as you get with `require/typed`. The bad news is that we don't even have a workaround 😃

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [November 17, 2021, 11:22pm UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/4 "2021-11-17T23:22:23Z")

</div>

I tried defining a couple functions from scratch, and I ran into the kind of obvious conclusion that type inference is not supported for the keyword arguments of polymorphic functions 😃

```
#lang typed/racket

(: foo (All (a) (->* (a a) () (Listof a))))
(define (foo x y) (list x y))
(foo 1 2)

(: bar (All (a) (->* (a a) (#:arg1 Number) (Listof a))))
(define (bar x y #:arg1 [z 1]) (list x y))
(bar 1 2)

(: baz (All (a) (->* (a a) (#:arg1 a) (Listof a))))
(define (baz x y #:arg1 [z x]) (list x y z))
(baz 1 2)
(baz 1 2 #:arg1 3) ; Type Checker: Inference for polymorphic keyword functions not supported

```

So, the problem kicks in when the type of the keyword argument involves a type variable.

* * *

OK, throwing away polymorphism works:

```
#lang typed/racket

(require/typed racket/hash
  [hash-union
   (->* ((Immutable-HashTable Symbol Number))
        (#:combine (Number Number -> Number))
        #:rest (HashTable Symbol Number)
        (Immutable-HashTable Symbol Number))])

(define h1 : (Immutable-HashTable Symbol Number) #hash((a . 1)))
(define h2 : (Immutable-HashTable Symbol Number) #hash((a . 2)))
h1
h2

(: combine-plus (-> Number Number Number))
(define (combine-plus a b) (+ a b))

(hash-union h1 h2 #:combine combine-plus)
(hash-union h1 h2 h2 #:combine combine-plus)
(hash-union h1 h2 #:combine +)

```

As you see, I can even use `+` directly, without hiding it behind `combine-plus`.

I guess I can just replace `Symbol` and `Number` with `Any` for now and help myself to `cast`s, but I hope a better solution exists.

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [December 2, 2021, 12:20pm UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/5 "2021-12-02T12:20:27Z")

</div>

For the record, here is the solution with which I plan to continue for now, after having tried 3–4 different things:

```scheme
#lang typed/racket

(module hash-union-submod racket
  (require (only-in racket/hash hash-union))
  (provide hash-union-list)
  (define (hash-union-list
           hts
           [combine
            (λ _ (error 'hash-union-list
                        "Don't know how to combine the values of duplicate keys."))])
    (apply hash-union #:combine combine hts)))

(require/typed 'hash-union-submod
  [hash-union-list
   (->* ((Listof (Immutable-HashTable Any Any)))
        ((-> Any Any Any))
        (Immutable-HashTable Any Any))])

(: combine-plus-any (-> Any Any Any))
(define (combine-plus-any x y)
  (+ (cast x Number) (cast y Number)))

(hash-union-list '(#hash((a . 1)) #hash((b . 1))))
(hash-union-list '(#hash((a . 1)) #hash((a . 1))) combine-plus-any)

```

In fact, what I need is applying `hash-union` to a list of hashes. What I do here is setting up a function `hash-union-list` which applies `hash-union` to a list with the `#:combine` keyword argument, in an untyped module `hash-union-list`. I then import `hash-union-list` into a typed module, replacing what might have been type variables by `Any`.

While this solution does the trick for me, it is underwhelming compared to this line of untyped Racket:

```scheme
(apply hash-union #:combine combine-plus '(#hash((a . 1)) #hash((b . 1))))

```

Furthermore, if I define the type of `hash-union-list` with the type variables `a` and `b`, I run into the same contract violation as mentioned above in this discussion.

I'd be very interested in any effort to bring proper typechecking to keyword arguments. Naively, I have the impression that it shouldn't be that hard to do, but I expect to be wrong. Unfortunately, my time is limited, but if there are "easy" tasks, I'd be very happy to contribute.

---

<div class="post-metadata">

**Author:** ![samth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samth/32/3_2.png) [@samth](https://racket.discourse.group/u/samth)\
**Post date:** [December 2, 2021, 4:49pm UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/6 "2021-12-02T16:49:40Z")

</div>

The best thing to contribute would be to add the `#:combine` argument to the type of `hash-union` which is here: [typed-racket/base-env.rkt at d65ff8bd7146b15b79f31506f8aca410aed196e4 · racket/typed-racket · GitHub](https://github.com/racket/typed-racket/blob/d65ff8bd7146b15b79f31506f8aca410aed196e4/typed-racket-lib/typed-racket/base-env/base-env.rkt#L1101)

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [December 21, 2021, 1:20pm UTC](https://racket.discourse.group/t/type-checker-inference-for-polymorphic-keyword-functions-not-supported/145/7 "2021-12-21T13:20:40Z")

</div>

Thanks @samth. I spend quite a bit of time thinking and reading `base-env.rkt` (and being busy with my day job 😃 ) and I think I can boil down my problem to the following two questions:

1. How is `->*` in `base-env.rkt` different from the [one which is exposed from the library](https://docs.racket-lang.org/ts-reference/type-ref.html#%28form._%28%28lib._typed-racket%2Fbase-env%2Fbase-types-extra..rkt%29._-~3e%2A%29%29)? The type of `hash-union` in `base-env.rkt` seems to imply that the type of the rest arguments is first given as a list, and then the type of the mandatory arguments is specified, which looks like the reverse of the description in the documentation. Also, a level of parentheses is missing with respect to what I'd expect (i.e.,`(-HT a b)` instead of `((-HT a b))`). So I am totally lost as to how I could add a keyword argument to that type. I tried looking for similar examples, but the best I could find is [`add-between`](https://github.com/racket/typed-racket/blob/d65ff8bd7146b15b79f31506f8aca410aed196e4/typed-racket-lib/typed-racket/base-env/base-env.rkt#L773) which uses `->key` instead of `->*`.

2. What is the best way to play with the development version of Typed Racket? Is `raco pkg install`-ing `typed-racket-lib` okay?
