notjack
2017-2-10 10:06:16

@stamourv changed my mind, I’ll just leave it the way it is and instead put that energy into writing something different :p


notjack
2017-2-10 10:06:26

next time I’ll be clearer about when it’s no longer a WIP though


mebassett
2017-2-10 13:43:31

is there any way to express this in typed/racket? (struct (a) Some ([v : a])) (: sneaky-hetero (Listof Some)) (define sneaky-hetero (list (Some 1) (Some "a")))

typed/racket obviously doesn’t like the above code. I am trying to express that I have a list of polymorphic data structures, and I want to retain the type of each item in the list - so (Listof (Some Any)) won’t do - but I don’t want to restrict which types my data structures’ type variables can be initialized to.


leafac
2017-2-10 14:00:26

As far as I know, Typed Racket’s type system is not able to express that (I might be wrong, though). But I guess occurrence typing (http://docs.racket-lang.org/ts-guide/occurrence-typing.html) might be able to get you half way there. Let (: sneaky-hetero (Listof (Some Any))) and then match on the elements as you get them out of the list.


pnwamk
2017-2-10 14:10:07

You might be able to do this by declaring the type of the field in the struct as an intersection of A and a union of the types you want to allow the struct to be initialized with


pnwamk
2017-2-10 14:11:14

But intersections are a relatively new feature, so it’s possible you might run into some use cases of intersections that haven’t been fully fleshed out yet (bug reports welcome! =)


mebassett
2017-2-10 14:19:38

alas, both the match and the intersection solutions would require me to be able to list all possible types a priori, which isn’t possible in this case. :disappointed:


leafac
2017-2-10 14:49:31

I believe you hit the edge of Typed Racket’s current capabilities. You want to abstract over the data container (the struct Some, in your case), which can’t be done in the current system. That’s the same reason why dict doesn’t exist in Typed Racket, one has to be specific about the underlying implementation of the dictionary (using hash-ref, assoc and friends).


pnwamk
2017-2-10 15:14:38

@leafac are you saying they could accomplish this task with generics (if they existed in TR)? I don’t think I fully understand the issue, and it’s not obvious to me at the moment why generics would solve this


pnwamk
2017-2-10 15:15:33

(I’m not trying to suggest they would not — just trying to understand)


leafac
2017-2-10 15:27:45

I was thinking, for example, of the following Java code:

import java.util.ArrayList;
import java.util.List;

class Some<T> {
        T value;

        public Some(T value) {
                this.value = value;
        }

        public T getValue() {
                return value;
        }

        public void setValue(T value) {
                this.value = value;
        }
}

public class Generics {
        public static void main(String[] args) {
                List<Some> sneakyHetero = new ArrayList<>();
                sneakyHetero.add(new Some(1));
                sneakyHetero.add(new Some("a"));
        }
}

samth
2017-2-10 15:29:03

@leafac that’s using raw types which is not really something we’d want in TR


leafac
2017-2-10 15:29:52

I’m afraid I don’t understand what you mean by “raw types.” Could you please expand on that?


samth
2017-2-10 15:31:25

Some in Java is both a type constructor, but also a type


samth
2017-2-10 15:31:41

when you use it as a type, it’s basically cheating and working like pre-generics java


leafac
2017-2-10 15:52:22

I understand it, thanks for the explanation. Indeed, I got some warnings from the code above. In conclusion, I was wrong: generics don’t give the expressiveness necessary to represent what @mebassett wanted. In other words, that problem is not related to dict. Is this right?


thinkmoore
2017-2-10 15:56:17

parametric types aren’t sufficient to represent heterogenous lists (and similarly, other heterogenous containers like dict)


samth
2017-2-10 15:59:25

@leafac I think @mebassett is thinking of existential types, which TR does not (directly) support


samth
2017-2-10 15:59:38

but the subtyping solution is the right thing for that problem, I believe


leafac
2017-2-10 16:08:26

@thinkmoore: I’m going to try to summarize what you said to confirm that I understood. Typed Racket’s All is equivalent in power to Java’s generics. They are both forms of parametric polymorphism. They are capable of expressing homogenous containers (for example, Java’s List) but not capable of expressing heterogeneous containers (for example, Racket’s dict). Did I get it?

@samth: When I learned about existential types (from reading Pierce’s book), I associated them with interfaces. How would they apply to the problem at hand? Also, what do you mean by “subtyping solution?”


samth
2017-2-10 16:08:49

I mean (Listof (Some Any))


samth
2017-2-10 16:09:09

and dict is not a heterogenous container any more than lists are


samth
2017-2-10 16:10:03

also, for both Java and TR, the “homogenous”/heterogenous distinction doesn’t make as much sense, because you can have List<Object> or (Listof Any) which can contain any value


leafac
2017-2-10 16:10:17

So the “subtyping solution” is to use occurrence typing. In other words, to match on the value as it comes out of the list. Right?


samth
2017-2-10 16:10:56

yes


samth
2017-2-10 16:11:20

well, it really depends what you’re trying to do with the elements of the list


leafac
2017-2-10 16:16:42

“and dict is not a heterogenous container any more than lists are”: The way I understand it, lists in Typed Racket are either homogeneous (for example, (Listof Integer)), or they are more like tuples, in the sense that it has a fixed size and each positing has a specified type (for example, (List Integer String)). (And tuples are really just Pair in disguise.)

So, how come Listof and HashTable are things, but dict is not?


leafac
2017-2-10 16:18:00

@thinkmoore, @pnwamk: You said generics don’t exist in Typed Racket, but parametric types do. I thought they were the same thing. Can you please help me get this straight?


pnwamk
2017-2-10 16:18:39

generics in Racket are sort of like inferfaces in Java — it’s orthogonal to parametricity


pnwamk
2017-2-10 16:19:01

we could imagine, if and when generics are added to TR, many will use parametric types


pnwamk
2017-2-10 16:19:05

but they wouldn’t have to


leafac
2017-2-10 16:20:26

And I thought interfaces in Java were related to existential types, but @samth just said Typed Racket doesn’t have them. As is usually the case when I try to think of type systems, I’m getting more confused :slightly_smiling_face:


samth
2017-2-10 16:20:55

there’s a confusion here because “generic” in Java refers to parametric polymophism, but “generic” in Racket refers to ad-hoc interfaces like dict


pnwamk
2017-2-10 16:20:56

he said “TR does not (directly) support”


leafac
2017-2-10 16:21:55

Oh, it’s getting clearer now :slightly_smiling_face:


leafac
2017-2-10 16:31:30

One more attempt to understand things: @mebassett wanted to create a list in which all elements are Some A, but for different A at each element. In Java, the solution for this is not use Java’s generics (parametric polymorphism), but to let Some be an interface, which all A would implement. This ties us back to existential types, which Java’s interfaces look like.

Now, Typed Racket supports parametric polymorphism (for example, (All (A) ...), but we just established that it does not solve the problem. What we want is Java’s interfaces, which we call Racket’s generics. And Typed Racket does not support Racket’s generics. So @mebassett is stuck with occurrence typing, which relies on subtyping.


leafac
2017-2-10 16:33:00

In conclusion, my initial observation was correct. @mebassett’s problem is related to dict. Both touch on Typed Racket’s lack of support for Racket’s generics (or Java’s interfaces, or existential types).


samth
2017-2-10 16:33:42

It sort of depends what @mebassett wants to do with the elements of the list


mebassett
2017-2-10 16:40:37

this discussion has evolved quite a bit from my original question (and has been nevertheless enlightening). the context of my problem was me trying to write around TR’s lack of generics. I was trying to write a way were one could “register” certain structures and casting operations so that one could dynamically cast those structures into JSExpr. id est, I wanted a (: dynamic-json-cast (-> Some-Jsonable-Type JSExpr)) My plan of attack was to have dynamic-json-cast search through a [mutable] list of (struct (T) Json-Cast-Datum ([predicate : (-> Any Boolean : T)] [->json (-> T JSExpr)])).


leafac
2017-2-10 17:13:08

I think I have a hack that might solve your problem. Instead of registering Json-Cast-Datum into a list, why don’t you make dynamic-json-cast an extensible function (https://docs.racket-lang.org/extensible-functions/index.html)?


leafac
2017-2-10 17:15:21

Everywhere you would mutate the list of Json-Cast-Datum, just extend dynamic-json-cast. This relies on occurrence typing to work, it’s the subtyping solution @samth was talking about. But it’s a convenient way to list the various cases in multiple places, instead of centralizing them. It even allows third-party libraries to extend dynamic-json-cast, if necessary.


stamourv
2017-2-10 17:15:56

@notjack: Sounds good.


leafac
2017-2-10 17:16:15

Disclosure: I’m the author of extensible functions :slightly_smiling_face:


mebassett
2017-2-10 17:22:59

@leafac thanks for the tip! I’ll give it a go, that might work.


leafac
2017-2-10 17:28:31

@mebassett: Thank you for considering it. Don’t mind the “dependency problems” reported by the package manager (https://pkgd.racket-lang.org/pkgn/package/extensible-functions). I already uploaded a fixed version, you can install it now. And tomorrow that warning is hopefully going to go away.


lexi.lambda
2017-2-10 17:37:56

Is there any reason not to make POD structs in my library serializable, in the racket/serialize sense?


lexi.lambda
2017-2-10 17:38:35

Someone opened a PR to make values from data/maybe and data/either serializable, which seems reasonable to me, but I don’t know if there are any downsides.


pavpanchekha
2017-2-10 18:13:30

Is there a way to create null output ports—I mean ports that just throw away all output?


pavpanchekha
2017-2-10 18:15:05

NM, it’s called open-output-nowhere and is in the miscellaneous section of the ports reference.