Search code examples
typesrackettyped-racket

Determine the specific type of an argument in Racket when the argument's type is a union type


Apologies for the messy title—my technical vocab is lacking, and I was unsure of how to phrase this. How can I identify an argument in Racket as being of a certain type, without knowing either

  • the constructors of the type (for use with match)
  • a function type? (such as integer?) that checks if it is the specified type?

Specifically, imagine I have some function with the type (: fn : (All (a b c) (a -> c) (b -> c) (Listof (U a b)) -> Listof c)); we can imagine it being called as (fn fa fb xs). I would like this function to convert xs into a Listof c; it should do so by mapping each element x of xs to (fa x) if x is of type a, and (fb x) if x is of type b. For example, we might take in a list of strings and reals, and map the strings to their lengths, and the reals to the nearest integers.

In the specific case where I know that a is String and b is Real, I can write something like

(define (fn fa fb xs)
    (map (lambda (x) (if (string? x) (fa x) (fb x))) xs))

But this works only if I have a known function string? to check the type of x.

Is it possible to do what I'm suggesting in Racket?


Solution

  • There's no "type of" operation in Typed Racket, so the answer is no. However, you can pass a "decider" to fn to select which function should be used to apply for each element.

    The -> form allows you to specify propositions for occurrence typing, so you can write this code:

    #lang typed/racket
    
    (: fn (All (a b c)
               ((U a b) -> Boolean : a)
               (a -> c)
               (b -> c)
               (Listof (U a b))
               ->
               (Listof c)))
    (define (fn decider fa fb xs)
      (map (lambda ([x : (U a b)])
             (if (decider x) (fa x) (fb x)))
           xs))
    

    Then you can invoke it:

    (fn string? string-length add1 (list 1 "a" 23 "bc"))
    
    ;; '(2 1 24 2)
    

    Thanks to Sam Tobin-Hochstadt for his help!