notjack
2021-1-2 12:09:21

What’s the advantage over having new-hash take a bunch of structs/tuples/lists/etc.?


kellysmith12.21
2021-1-2 12:15:45

The advantage over structs is that you don’t need a separate data type for each kind of grouping. The advantage over lists is that this is an explicit grouping mechanism, and I prefer to use lists just for sequences. I’d consider anonymous tuples an equally valid solution, and, unless there’s a significant performance difference, I’d say the choice between the two is largely one of style.


notjack
2021-1-2 12:17:04

Oh I want a separate data type for each kind of grouping, because each kind of grouping has different names for the parts of the group. key and value are much better names than first and second (when working with hashes).


notjack
2021-1-2 12:18:12

those names need to be able to show up in error messages, especially


kellysmith12.21
2021-1-2 12:18:47

Ah, from my perspective, it’s not so much of an issue because users of my lang would use patterns to destructure the tuples, instead of accessor functions.


notjack
2021-1-2 12:20:24

Would patterns be required for destructuring even in a higher order manner? Like (map (lambda ((group key _)) key) entry-groups) instead of (map entry-key entries)


kellysmith12.21
2021-1-2 12:25:04

Hm… that’s one place where having specific structs does have an advantage of concision. With anonymous tuples, you could write your own convenience wrappers, but that’s about it.


notjack
2021-1-2 12:27:04

and your convenience wrappers would probably be something like tuple-first rather than entry-key


kellysmith12.21
2021-1-2 12:28:09

I’d say it’d be ok to use a specific name, like entry-key, because in the given context, the tuple has a specific meaning, despite being a very generic data type.


notjack
2021-1-2 12:29:44

It’ll be tricky to convince people to define the exact same function multiple times in different places with different names. They’ll want to scratch the DRY itch too badly.


kellysmith12.21
2021-1-2 12:30:34

true


kellysmith12.21
2021-1-2 12:36:14

My concern about using specific types for each use case is a proliferation of isomorphic types that need explicit interconversion, potentially leading to clutter. However, I have no idea if that would actually be a problem, without a large code base to evaluate.


notjack
2021-1-2 12:36:57

It’s a problem, but the problem is adequately solvable (IMO) with macro-assisted converter generation


notjack
2021-1-2 12:39:12

Like if I can do this: (define-tuple-type pair (first second)) (define-tuple-type entry (key value)) (define-tuple-converter pair entry) > (pair->entry (pair 1 2)) (entry 1 2) > (entry->pair (entry 'a 5)) (pair 'a 5) Then all is well as far as I’m concerned.


kellysmith12.21
2021-1-2 12:40:34

I do love how Racket lets us throw macros at our problems and it generally solves them quite nicely :smile:


kellysmith12.21
2021-1-2 12:43:08

I think that’s a tidy solution, so my concern about using specific types for grouping has been assuaged.


notjack
2021-1-2 12:44:29

full disclosure: I’m biased because I worked with the guy at google who wrote the @AutoConverter annotation processor, which is what the whole google java codebase uses for exactly this kind of isomorphic value type converter generation


kellysmith12.21
2021-1-2 12:52:55

The automatic converter generation eliminates the boilerplate, and although the explicit conversions add a little verbosity to the code, the specific types will make contract violations and type errors more descriptive.


selcukahmed
2021-1-2 12:54:00

@selcukahmed has joined the channel


kellysmith12.21
2021-1-2 12:55:43

I think it’s a well-balanced solution.


notjack
2021-1-2 12:58:22

glad I could convince you :smile:


kellysmith12.21
2021-1-2 13:16:31

Speaking of types, I’m still trying to figure out the API for type definitions in my lang. I know that I want to use rebellion/type instead of struct, but I’m not certain how I should expose it. I’m considering two options: 1. Simply reprovide define-tuple-type, &c., wrapped to handle my lang’s delimiter-sensitive syntax. 2. Provide a define-type form for defining algebraic data types, which expands to the Rebellion type definitions and generates an appropriate predicate and contract for the whole type. Personally, I’d prefer to go with 2, but I’m not certain how to handle things like struct properties, and (hopefully) generic interfaces.


kellysmith12.21
2021-1-2 13:19:48

One solution for 2 is, instead of having the property or interface implemented piecewise for each case of the type, have a single definition that pattern-matches on the type cases, which is stored in an auxiliary definition that the resulting define-x-type forms refer to.


notjack
2021-1-2 13:29:48

Good question. I think 2 is worth attempting, at least.


notjack
2021-1-2 13:29:55

Not sure what the best approach would be.


kellysmith12.21
2021-1-2 13:33:55

A slightly different approach would be to arrange for the types to all derive from an empty struct supertype that implements the properties and generics. Although, I don’t know if that can be done with Rebellion’s type interface.


notjack
2021-1-2 13:51:08

Ah, rebellion’s type interface explicitly does not support subtyping


kellysmith12.21
2021-1-2 13:53:24

Just as well, I don’t want to allow subtyping, this use would’ve been an implementation trick.


kellysmith12.21
2021-1-2 13:54:30

I’ll try the approach that delegates to an auxiliary function and report back.


kellysmith12.21
2021-1-2 14:02:32

In the meantime: I’ve made a macro for dependent function contracts. Racket version: (->i ([x number?] [y (x) (>=/c x)]) [result (x y) (and/c number? (>=/c (+ x y)))]) My version: {λΠ/c x : number? y : {Π/c x ⇒ (number≥/c x)} → result : {Π/c x y ⇒ (and/c number? (number≥/c (+ x y)))}}