-
Notifications
You must be signed in to change notification settings - Fork 1
/
rust.txt
1286 lines (1104 loc) · 54.6 KB
/
rust.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
SUBMITTED
Checked arithmetic
Ban private items in public APIs
Turn capitalization conventions into language rules for 1.0
Change `extern "ABI"` to `extern<ABI>` or `extern ABI`
Use `for` to introduce universal quantification
NEXT UP
Further privacy reform
Existentially Quantified Types
Deconflate types and modules
Purity
Exceptions
Allow #attr and #attr(foo) but still require #[attr = foo]
foo::<T>() -> foo@<T>()
Inline modules use normal scoping
trait Transmute
Garbage collection (trait Trace)
&out
&move
DISCOURSE?
`box`
Deterministic name resolution
Macro reform
Intrusive datastructures, unmovable types
Linear types
Philosophy
# RFCs
# CRITICAL
* Existentially Quantified Types (IN PROGRESS)
* `struct Foo<T: Trait>` `&Foo` for thin pointer to structs with internal vtables
* String literals are unboxed rvalues
* ...
* Purity for (almost) free (FIGURED OUT)
* Deconflate types and modules, UFCS, UMCS, ... (FIGURED OUT)
* Closed structs, enums, traits (`abstract struct`, `data struct`, `class`, whatever) (IN PROGRESS)
* Deterministic name resolution (AKA "No guessing")
* Closure reform (SUBMITTED BY nikomatsakis)
# BIC
* Make inline `mod`s obey normal scoping rules
Distinguish inline (in-file) vs. external (separate file) modules
Items of (in-same-file) parent modules are in scope for inline mods, `use` uses relative paths. Like C++.
inline mods: `mod foo { ... }`, out-of-line mods declared: `extern mod foo;` (but bikeshed)
QUESTION require :: to refer to crate root in `use`? is the crate root in scope everywhere by default?
crate { lib.rs: mod parent { extern mod foo; } foo.rs: { ...BODY... } }
=> crate { mod parent { use foo; ... } mod foo { use super = parent; ... BODY ... }
? Remove subtyping and variances (only sublifetimes, but not sub*type*s?)
? Automatic coercions reform (`as` reform?)
* Linear types
Unwinding? Require explicit `Drop` bounds?
* Parametricity
* QUESTION: how to reconcile with trait SizeOf<T> { static N: uint } forall types?
trait SizeOf<T> { static SIZE_OF: uint; };
unsafe fn size_of<T>() -> uint;
is this a problem for static declarations?
or just have a `sizeof` operator which is `unsafe`?
* QUESTION how to deal with Drop? require it explicitly?
* Checked arithmetic (SUBMITTED)
* Ban private items in public signatures (KINDA ACCEPTED BUT NOT REALLY)
* Tighten shadowing rules (SUBMITTED BY Kimundi)
* Enforce capitalization conventions (REJECTED)
Convention over configuration!
* *T should be non-nullable
`Option<fn()>` is *already* required in FFI! Should just guarantee repr of `Option`-like generic types with FFI-relevant type arguments.
* Syntax wibbles:
Struct { f: v } -> Struct { f = v }
#[attr] -> #attr (like hashtags => suggestive of metadata)
name@pat -> pat as name
<'a> |&'a Foo| -> for<'a> |&'a Foo|
&'a mut Foo -> &mut 'a Foo
extern "C" -> extern C, extern<C>, or extern(C) (SUBMITTED)
foo::<T> -> foo@<T> (remove confusion w/ module scoping)
fn foo<'a> -> fn foo<lifetime a> or fn foo<scope a>
&'a Foo -> &(a) Foo or &{a} Foo (braces are suggestive of scopes)
# IMPORTANT
* "Mutability polymorphism"
* Exception handling a la Either monad (FIGURED OUT)
* trait Trait1 + Trait2 (PARTIALLY IMPLEMENTED)
* trait Transmute<T> (SUBMITTED BY gereeter)
* GC, trait Trace
* &move
* &out (SUBMITTED BY gereeter)
* "Unboxed trait objects" (SUBMITTED BY aturon, POSTPONED) (might be subsumed by EQT!)
* Type ascription
* Associated types (SUBMITTED BY aturon)
* Macro reform
# MINOR
* General function-like generics syntax sugar
type Proc(A) -> R = Box<Send + FnOnce(A) -> R>;
QUESTION where to put lifetimes (&mut FnMut...)
(general guideline: use for things that (could) impl... Fn? Category? Profunctor?)
* UnsafeUnion<A, B>
? Incoherent empty traits
* Associated statics (SUBMITTED BY aturon)
? Anonymous sum types (int|bool|char)
* enum bool { false, true }, all two-nullary-variant enums repr.-ed the same as a bool (BIC?)
generally: guarantee all merely-alpha-renamed structs, tuples, enums repr.-ed the same? (no reason not to)
* Early return from any block (like `break`, but not just loops, and with a value)
* Explicit refutable `let`: `let`..`else`
* Scoped refutable `let`: `if let` (SUBMITTED BY kballard, ACCEPTED)
* Expose tuple fields as .0 .1 ... .N (SUBMITTED BY P1Start, ACCEPTED)
* Array splicing
* Fixed-length array slices in patterns (SUBMITTED BY krdln)
* Destructuring assignment
* Disjunctive patterns in `let`s (SUBMITTED BY P1start, POSTPONED)
* Liberalized destructing struct destructuring
? Limited type inference for `static` items
* #compact struct, enum
? Generalized unboxed object literals (closures) for any trait, not just `Fn`*
QUESTION syntax: impl-like? closure-like? struct-like?
let num = 42;
let showable_obj = &Show {
show: num.to_str()
};
self is implicit?! like fn closures
see `BiOnceFn` in Exceptions for compelling use case!
let my_bi_fn = box BiOnceFn {
call1(n): num + n,
call2(m): num * m
};
* for<T1..TN> { ..items.. } (SUBMITTED BY ben0x539, POSTPONED)
* Naming wibbles:
macro_rules! -> macro!, define!
Any -> Dynamic
Arc -> AtomicRc
Option -> Maybe (Optional, Nullable)
Some|None -> Yes|No (Full|Empty, Val|Null)
unwrap -> assert (goes well w/ Yes|No)
Void -> Never
int, uint -> isize, usize
char -> Char (no need to confuse w/ C char)
Iterator -> Iterate? (or maybe Iterable -> Iterate)
# FAR FUTURE
* `static` generics parameters
* Generic generics parameters (HKT)
* Partial type application
* Trait generics parameters (ConstraintKinds)
* Kind polymorphism
* First-class polymorphic types (higher-rank types)
* Explicit existential quantification syntax
* Existential quantification in structs
* Polymorphic recursion
* `static fn`, CTFE
? Dependent types a la pigworker
* Datatype-generic programming (GHC.Generics)
* Variadic generics
* TCE
? Delayed initialization of struct fields (loop tying)
## QUESTIONS
* What would break without subtyping for lifetimes?
* What's the conflict between modules and macros?
* Why isn't a simple newtype-based design sufficient for HashMap?
* Is `Arrow` connected to linear logic/types? (To closed (symmetric) monoidal categories?)
* Is there a relationship between traits and copatterns/codata? https://github.com/idris-lang/Idris-dev/wiki/Copatterns
* Is there a description/paper about UHC's implementation of existential types?
* How can safety of (multiply-)intrusive data structures be proved / reasoned about in general?
* https://github.com/rust-lang/meeting-minutes/blob/master/workweek-2014-08-18/associated-items.md
=> What are path dependent types?
Same thing as this? http://stackoverflow.com/questions/2693067/what-is-meant-by-scalas-path-dependent-types
Where do they occur in Rust? (Something to do with trait objects?)
* Is it possible that we might want bidirectional type inference instead of HM?
The biggest "drawback" usually cited for BiDiTI is that it requires type signatures on top-level definitions.
But we already require that!
## LANG ITEMS PROBLEM
There are many language features which would be backwards-compatible additions, except that they would, or should, have an impact on various lang item traits => scope creep. Is there any good resolution to this problem?
Examples:
* `Fn`* want associated types.
* `Fn`* may want arity polymorphism.
* `Iterator` wants associated types and `&move`.
* `Deref`* and `Alloc` (for `box`) want HKTs.
* `Alloc` wants `&out`.
* `Deref`, `Fn`, `Index` want mutability polymorphism!
Just declare these unstable, and move on? Or what?
Specific features:
* We're already adding assoc types.
* If it's not much work on top of static drop analysis, we should just add `&out` (and `&move`?).
* HKTs are harder.
* Arity polymorphism: definitely not yet.
* Mutability polymorphism: seems like a big change.
Likewise for standard library... if we want to add everything that might impact std before 1.0, we're going to end up adding /every/ major feature before 1.0.
If a feature didn't have an impact on libraries, it wouldn't be major.
Only real solution is to version stdlib.
## How `box` should work
// should prob be called something different if we're going to have a separate trait for allocators
// but maybe that should be called RawAlloc or w/e
trait Alloc {
type Where = ();
fn alloc<T>(self: &out Self<T>, where: Where) -> &out T; // HKT
}
impl Alloc for Box { ... }
impl Alloc for Rc { ... }
impl Alloc for Gc { ... }
struct Arena { ... }
struct ArenaRef<'s, T> { ... }
impl<'s> Alloc for ArenaRef<'s, ..> { // partial type application
type Where = &'s mut Arena;
...
}
`box(where) some_foo` =>
* type determined by inference
* type is `B<Foo>` such that: `some_foo: Foo` && `where: Where` && `B: Alloc` && `B::Where = Where`
* `box some_foo` short for `box(()) some_foo`
* `B` defaults to `Box`?
* use type ascription to fix `B` when necessary, e.g. `let rc_foo: Rc<_> = box some_foo;`, or later `box some_foo: Rc<_>`
HKT is important so the element type can be inferred with _, while outer type is specified; otherwise, have to specify whole type!
(question: precedence of `:`?)
some_foo: Foo
where: Where
--
box(where) some_foo: forall B: Alloc where B::Where = Where. B<Foo>
box(where) some_foo
=>
{
let the_box;
*the_box.alloc(where) = some_foo;
the_box
}
box some_foo
=>
box(()) some_foo
??
trait Dealloc {
fn dealloc<T>(self: Self<T>, move_to: &out T);
}
connection to Drop??
is this the same thing as DerefMove?
can _temporarily_ move out of smart pointers with DerefMut by general mechanism of moving out of &mut, leaving an &out
moving out permanently prob needs Dealloc/DerefMove like above
## MUTABILITY POLYMORPHISM
move mutability from reference types onto lifetimes
this is what Disciple does!!
lifetimes can have constraints e.g. "mutable", "unaliased", "shared", w/e
("lifetime" nomenclature stops being so helpful - difficult to envision a lifetime having a mutability; a "region" less so)
instead of current 2 reference types (+ more in the future), would have only one, parameterized over a lifetime, which in turn over mutability
can be either polymorphic over constraints-on-lifetimes or specify constraints; constraints can conflict
current set of constraints would be: shared, mut (which conflict)
this would essentially be reifying existing borrow checker rules/logic into language constructs
can mostly (entirely?) keep existing (quite nice) surface syntax as sugar for more sophisticated underlying mechanisms
example: instead of separate `Deref` traits for shared and mutable refs, could have just a single one
trait Deref<constraints cs> {
fn deref<T, 'a: cs>(&'a Self<T>) -> &'a T; // also HKT here, but orthogonal
}
impl<constraints cs> Deref<cs> for Box { ... } // equiv of both existing `Deref` and `DerefMut`
impl Deref<shared> for Rc { ... } // equiv of just existing `Deref`
could also `impl Deref<mut> for ...` if that's ever desired (just `DerefMut`)
likewise for `Fn`, `Index`, ...?
could extend lifetime elision to also elaborate mutabilities/constraints
with `&?" to denote a reference of unspecified constraints (plain `&` continues to mean "shared"):
fn get_member(self: &? Foo) -> &? Bar;
elaborates to:
fn get_member<constraints cs, 'a: cs>(self: &'a Foo) -> &'a Bar;
putting the constraint in the ref type is just sugar for the same constraint on the lifetime
`fn foo(&mut int)` -> `fn foo<'a: mut>(&'a int)`
`fn foo<'a>(&'a mut int)` -> `fn foo<'a: mut>(&'a int)`
built-in traits on `&` determined by constraints on lifetime
impl<T, 'a: shared> Copy for &'a T { }
can expand the set of constraints to "add more reference types", with repercussions
`uniq`: conflicts with `shared`, implied by `mut`
`read`: can be read from ("is initialized"). implied by `shared`, `mut`, `move`
`move`: "is initialized, must be deinitialized" implies `mut`, `uniq`, and `read`
`out`: "is uninitialized, must be initialized" implies `uniq`, conflicts with `read`
has further ramifications for built-in traits & "default assumptions"
a ref with `out` lifetime must be used linearly
can't consume `move` without destroying referree
instantiating above fully-polymorphic impl Deref for Box with `out` or `move` would **not** be safe
therefore, the definition as provided must be considered illegal? (or not, because uses `unsafe`?)
to make it safe&legal, would prob need something like:
impl<constraints cs: read> Deref<cs> for Box { ... }
but still not enough: must somehow rule out `move`!
(maybe each lifetime should be parameterized over *two* constraints: a "before" and an "after"?
might help reduce proliferation of types of constraints. e.g. instead of `out`, have before: init, after: uninit
in this case the `Box` impl could specify that both before & after must be alive, which hopefully should be sufficient)
could potentially fold unsafe/raw pointers under this umbrella as well
`safe` and/or `unsafe` would become further constraints
derefing a ref with `unsafe` (or not `safe`?) lifetime would require an `unsafe { }`
`mut` would no longer imply `uniq`, and would be separate (to support `*mut`)
probably further refinements required to support read-only `*const` vs. usually-immutable `&`
in expression language, /could/ make `&` operator be polymorphic over constraints, thus inferred from use site
but probably don't want to!!
functions being usable with different reference types is valuable
`&` operator being usable with any concretely-constrained function is not, just hurts readability
(would also lead to ambiguity w/ constraint-polymorphic functions, but could just default to "shared")
## MACRO REFORM
- fully hygienic
- privacy according to defn site
- _all_ name resolution in macro bodies should happen at definition site! (syntactic closure)
=> hygiene falls out as a consequence
- namespaced like other items
hard because macro expansion happens before name resolution
doing them together is allegedly very difficult
could they alternate? make progress as far as possible, then call back to the other?
=> a topological "traversal"
with some kind of restriction to rule out cycles
or could possibly just detect cycles & report them as errors, like w/ name resolution prototype?
is the restriction necessary? analogous to the stage restriction in Template Haskell
but is that only because TH macros are actual Haskell code which must be compiled, unlike macro_rules!?
i.e. they are procedural macros
is it avoidable for declarative macros?
has two parts:
1. can only use macros imported from a different module (~ different crate requirement for procedural macros in Rust)
2. top-level splices act as a barrier
## REMOVE SUBTYPING?
https://github.com/rust-lang/rfcs/pull/192#issuecomment-52168355
or rather, remove the need for variance inference / annotations?
TENTATIVE PLAN:
type parameters are always invariant
use `trait Transmute`, safe-`transmute()` to explicitly coerce compatible types
lifetime parameters are always contravariant (like `&`)
is there any use case _whatsoever_ for a closure or fn type where the lifetime of an arg is fixed instead of polymorphic?
e.g. do you _ever_ want `fn foo<'a>(f: fn(&'a Foo) -> ...)` instead of `fn foo(f: <'a> fn(&'a Foo) -> ...)`?
motivation:
* avoid the need for variance annotations on abstract types
* free up complexity budget for other things (e.g. EQT)
## TRANSMUTE
(originally Coercible)
featherweightmusings.blogspot.hu/2014/03/subtyping-and-coercion-in-rust.html
no subtyping
only Transmute trait + variances/roles
Transmute is explicitly declared for abstract `struct`s, just like the other built-in traits
three different conversioney things:
Transmutes:
zero-runtime-cost reinterpretations
Automatic coercions:
those which only involve throwing information away
casting away concrete types into existentials
casting numeric types to strictly larger numeric types
u8 -> u16 i16 u32 i32 f32 u64 i64 f64
i8 -> i16 i32 f32 i64 f64
u16 -> u32 i32 f32 u64 i64 f64
i16 -> i32 f32 i64 f64
u32 -> u64 i64 f64
i32 -> i64 f64
f32 -> f64
how to extend this to user-defined types?
Casts with `as`:
somewhat ad-hoc?
int-float conversions based on numeric value
like for hypothetical automatic coercions, but in any direction
how to extend to user-defined types?
if T is:
free => no conditions
covariant => T: Coercible<U>
contravariant => U: Coercible<T>
invariant => T: Coercible<U> && U: Coercible<T>
fixed => !
what about `ref` roles? part of this hierarchy or orthogonal?
free: always ref
fixed: never ref
co, contra, invariant: ?
instead of variances on type parameters, use generic `Transmute` impls!!
instead of `struct Foo<bivariant T>`:
impl<U> Transmute<Foo<U>> for Foo<T>
instead of `struct Foo<covariant T>`:
impl<U, T: Transmute<U>> Transmute<Foo<U>> for Foo<T>
instead of `struct Foo<contravariant T>`:
impl<U, T: Transmute<U>> Transmute<Foo<T>> for Foo<U>
instead of `struct Foo<invariant T>`:
impl<T, U> where T: Transmute<U>, U: Transmute<T> Transmute<Foo<U>> for Foo<T>
instead of `struct Foo<fixed T>`:
nothing
instead of `struct HashMap<K, covariant V>`:
impl<K, V2, V1: Transmute<V2>> Transmute<HashMap<K, V2>> for HashMap<K, V1>
`impl<T, U: HasPrefix<T>> Transmute<&U> for &T`
`impl<T, U: Transmute<T>, static N: uint> Transmute<[U, ..N]> for [T, ..N]`
mass borrowing would require forall-constraints, but can be done with Skolems
can this be done entirely as a library?? (except for the auto-deriving and the more-magical built-in impls..?)
impl<T> HasPrefix<()> for T
impl<T> HasPrefix<T> for Void // or even Transmute?
## LINKS
Inferring Safe is wrong (abstract vs. data type distinction)
https://ghc.haskell.org/trac/ghc/ticket/8827
related http://ezyang.tumblr.com/post/96448232652/safe-zero-cost-coercions-for-haskell-richard
FlexibleInstances can be used to violate coherence!!
http://www.reddit.com/r/haskell/comments/2agy14/type_classes_confluence_coherence_global/civ6y1g
TODO port test case to Rust!
related http://blog.ezyang.com/2014/09/open-type-families-are-not-modular/ (ragamuffin instances)
Macro import/export
https://github.com/mozilla/rust/issues/3114
Meeting discussion about macros
https://github.com/rust-lang/meeting-minutes/blob/master/Meeting-other-2014-07-16.md
Adding dependent types to Haskell (pigworker = Conor McBride)
http://stackoverflow.com/a/13241158
Full dependent types in Haskell (w/ singletons)
http://lpaste.net/109388
Niko Matsakis's name resolution plan + prototype
https://github.com/nikomatsakis/rust-name-resolution-algorithm
API stability discussion, w.r.t. deterministic name resolution
https://github.com/rust-lang/meeting-minutes/blob/master/workweek-2014-08-18/api-evolution.md
Syntactic closures
http://www.rntz.net/post/intuitive-hygienic-macros.html
GHC kind inference
https://ghc.haskell.org/trac/ghc/ticket/9200
Freestanding implementation of OutsideIn
https://github.com/batterseapower/outside-in
Hindley-Milner elaboration in applicative style
http://ezyang.tumblr.com/post/96449381857/hindley-milner-elaboration-in-applicative-style
Implementation strategies for polymorphism
http://www.eecs.harvard.edu/~greg/cs256sp2005/lec15.txt
Ericson2314's modules/traits proposal
TOREAD https://github.com/Ericson2314/rfcs/blob/master/active/0000-combine-mod-trait.md
related http://ezyang.tumblr.com/post/96605373187/1ml-core-and-modules-as-one-or-f-ing-first-class
related http://www.reddit.com/r/haskell/comments/2foggq/why_does_david_turner_say_type_classes_were_a_bad/ckbw3g7 (neelk)
related http://www.reddit.com/r/haskell/comments/2foggq/why_does_david_turner_say_type_classes_were_a_bad/ckchsx5 (edwardkmett)
TCE
https://mail.mozilla.org/pipermail/rust-dev/2011-August/000711.html
https://mail.mozilla.org/pipermail/rust-dev/2011-August/000700.html
https://mail.mozilla.org/pipermail/rust-dev/2013-April/003557.html
https://github.com/mozilla/rust/issues/217
TOREAD Variadic generics / arity polymorphism
http://www.ccs.neu.edu/racket/pubs/NU-CCIS-08-01.pdf
http://www.ccs.neu.edu/racket/pubs/NU-CCIS-08-03.pdf
http://www.ccs.neu.edu/racket/pubs/esop09-sthf.pdf
http://www.ccs.neu.edu/racket/pubs/dissertation-tobin-hochstadt.pdf
rwbarton on type lambdas
http://www.reddit.com/r/haskell/comments/urc75/complicating_conduit/c4xyqiz
edwardkmett on the mistakes of Scala
http://www.reddit.com/r/haskell/comments/1pjjy5/odersky_the_trouble_with_types_strange_loop_2013/cd3bgcu
implicit lambdas http://www.reddit.com/r/haskell/comments/2fbe4t/compose_to_avoid_lambdas/ck7vzcs
BitC Lessons For Other Language Developers - Simple vs. Too Simple
http://www.coyotos.org/pipermail/bitc-dev/2012-May/003469.html
Exceptions
https://mail.mozilla.org/pipermail/rust-dev/2013-November/006563.html
http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx
http://twistedoakstudios.com/blog/Post399_improving-checked-exceptions
http://www.reddit.com/r/haskell/comments/2csaml/are_there_any_new_developments_in_the_last_seven/
http://blog.ezyang.com/2011/08/8-ways-to-report-errors-in-haskell-revisited/
http://hackage.haskell.org/package/control-monad-exception
http://www.jot.fm/issues/issue_2011_01/article1.pdf
http://erdani.com/publications/cuj-2003-12.pdf
Existential types
TODO add papers
Higher-rank types
TODO add papers
## INTRUSIVE DATA STRUCTURES
requires non-moveable types!?
what would the ExplicitMove trait look like?
because passing by value (moving) is impossible, pass by &move?
can't actually move out of the &move, but can consume it and copy contents
trait Relocate {
fn relocate(from: &move Self, to: &out Self);
}
implicit movability is turned off by impl Relocate?
do you ever want Relocate and not Clone?
Relocate is equivalent to a Clone followed by a Drop (but more efficient)
reason normal movable types + pinning by borrowing it is not suitable:
if it's borrowed it can't be moved to a new location explicitly either
`relocate()` maintains dynamic invariants through user code
idea in Rust w/ everything movable is that values can have pointers out from them, but not into them
except in cases where it's statically tracked by the borrow checker
but this doesn't allow for constructs where invariants are upheld dynamically at runtime
is this also connected to the fact that you can only safely express (sendable) tree structures?
http://www.reddit.com/r/rust/comments/2ac60j/safely_storing_a_selfreference_in_self/ciu7wtw
is this related to arena allocation?
yeah.. arenas shouldn't be movable
but maybe that's handled by having `&` references into them?
but then they're not `Send`, which is the point?
generic functions?? :(
## &out, &move
Expressiveness delta relative to pass-by-value and return-by-value:
Explicit lifetimes, tying together
Trait objects
Slices
Uniform representation
Flexible timing of returns / moves
## Deterministic name resolution
"no guessing"
QUESTIONS: is seeing names from outer scopes still deterministic? why?
conjecture: safe to add items, but not remove them
what's the precise definition of "deterministic"?
an extensional definition: such that adding (removing?) items doesn't cause silent behavior changes
can we give an intensional one, i.e. what it precisely is that we're not allowed to do? (i.e. guessing/branching)
same-scope shadowing problem
this is being solved
autoderef problem:
method calls on things that impl Deref *always* deref
to call fn on ref/ptr/box itself, use UFCS
would auto cross-borrowing (smart Deref ptr -> &) cause any issues here?
autoref problem:
TODO is there a problem?
method resolution + impls problem:
should _not_ search trait impls when resolving methods
trait methods are like any other generic fn
patterns + enum variants & statics problem:
enforcing capitalization conventions globally would be best, but...
a narrowly targetted solution:
(irrefutable patterns can be left alone I think - don't think it's possible for it to cause a difference?)
in refutable patterns, compiler *always assumes* uppercase is an enum/static, lowercase is binding!
(definitely in `match`, need to think about `if let`)
if you want to have an uppercase binding or lowercase enum/static, need to explicitly say so
e.g. `Some(as MyBinding)` (esp. if/when `name@pat` becomes `pat as name`), `enum my_variant`, `static my_static`, or something like that
(bikeshed...)
this is butt ugly!! but: so is the current situation!!!
rarely-encountered ugly syntax is lesser evil than rarely-encountered ugly semantics:
only the latter causes bugs
there's still the hardline "case should never be significant" position, but if both sides standing on principle (i.e. not much practical difference), correctness is the more important principle
## DOM
> constraints:
> cheap field access from internal methods;
> cheap = constant offset (fixed prefix layout)
> less cheap = offset in vtable
> expensive = virtual method
> cheap dynamic dispatch of methods;
> "cheap" = current virtual methods
> expensive = going through an indirection for each link in the inheritance chain, i.e. a non-flattened tree-like structure
> cheap downcasting;
> cheap = TypeId in vtable
> expensive = going through a virtual method to retrieve a TypeId (or worse)
> thin pointers;
> sharing of fields and methods between definitions;
> safe, i.e., doesn't require a bunch of transmutes or other unsafe code to be usable.
relevant features:
trait Transmute
EQT (thin pointers, downcasting (Any/Dynamic w/ associated static TypeId))
closed traits (cheap "dynamic dispatch" (not even dynamic), downcasting)
TODO translate https://gist.github.com/jdm/9900569
## Misc features
- allow `loop { }` to evaluate to a value by providing it to `break`
(probably not `while` and `for`... what's the value when the loop stops?)
related to "early return from any block"
- allow assigning to return value before exiting function?
fn foo() -> int {
return = 9;
println!("howdy");
}
as-if it were `fn foo(&out int)`
but maybe this particular syntax doesn't work
also strange w.r.t. types... last evaled expr wouldn't match return type
- short-circuiting ptr-equality comparison for `impl Eq for &`?
needs some kind of magic because of PartialEq, e.g. `{ let f = NaN; &f == &f } == false` even though ptr-equal
maybe rewrite rules?
- attributes to specify type class laws.. or first-class language feature? for fns in general, not just type classes
- allow inferred type-wildcards (_) in `impl` method signatures?
- `fn swap<T>(self: &Cell<T>, other: &mut T)`? (no Copy!)
- `EXPR is PAT: bool` operator
- currying: foo(1) if foo is a two-arg callable thing returns a one-arg callable thing (unboxed closure)
- refutable patterns in for loops (obsoleted by `while let`!)
- destructuring in assignments
- array splicing [1, 2, ..arr] [..[1, 2, 3], 4, 5, ..[6, 7], 8, 9, ..[10]]
can only splice fixed-length arrays into fixed-length arrays
- early return from any block: return 's val? break 's val?
default for `break` is innermost loop, not block. is that intuitive? for `return` is outermost block.. `'fn`
- compacted enum & struct:
would use most compact possible representation the compiler can devise
i.e. sizeof should not usually be greater than log2(number of logical inhabitants)
tradeoff: can't have references to interior of compacted enum/struct
instead, when taking an &ref to field, unpack and copy it out onto stack first (like rvalues)
for &mut ref, the same, then pack and copy it back afterwards!
because of rigorous control over lifetimes, this should work
also need to make sure copy-back happens during task failure?
would remove need for manual bit twiddling whenever the goal is a compact, efficient representation
such as flag bits, e.g.:
#repr(compact)
struct Flags { flag_a: bool, flag_b: bool, ... flag_h: bool }
=> sizeof(Flags) == 1
could also take advantage of tag bits in pointers, i31, ... go crazy
would not supplant manual bit twiddling if a _specific_ representation is required, e.g. for hardware interaction
(but maybe allow specifying explicitly in #repr args?! would need to be pretty sophisticated...)
- allow type inference of locally declared fns and statics?
- deriving Functor, Contrafunctor, Bifunctor, Profunctor, .. ?
- all built-in types just sugar for lang items
nice for documentation & macros & general niceness
#lang(const_ptr) struct ConstPtr<T>
#lang(mut_ptr) struct MutPtr<T>
#lang(shared_ref) struct Ref<'s, T>
#lang(mut_ref) struct MutRef<'s, T>
#lang(array) struct Array<static N: uint, T>
#lang(str) struct str<static N: uint> { data: [u8, ..N] }
#lang(char) struct char { data: u32 }
#lang(bool) enum bool { false, true }
...numeric types...
what about `fn`? tuples?
- let .. else ..
- also important to enable e.g. let &mut [mut ref a, ..] = my_mut_arr else fail!();
- ! as first class type (is just forall a. a)
- disjunctive patterns in lets
- anonymous sum types
- DefaultSignatures
- unsafe traits and and unsafe impls? (can break variant that isn't directly unsafe, but other thing relies on it for safety)
- BIC? modular 'unsafe'? unsafe(memory), unsafe(array), unsafe(overflow), unsafe(rc_cycles), unsafe(concurrency)
- would require mapping the dependencies
- unsafe overlapping/incoherent impls, assumed confluent?
## Lazy<T>
- for any given function with a parameter Rust wants a version where it's a value, and one where it's a closure
- Haskell values are lazy, so it gets away with a single definition
- ...whereas these Rust versions would want to use once fns
- a lazy value only gets evaluated once
- blackholing ~ borrow checking? (RefCell)
- the FnOnce bound should only be necessary at construction!!
- in: FnOnce, out: FnMut
pub struct Lazy<F, T> {
eval: fn(&mut Lazy<F, T>) -> &mut T,
data: UnsafeUnion<F, T>
}
impl<F: Copy, T: Copy> Copy for Lazy<F, T> { }
impl<F: Send, T: Send> Send for Lazy<F, T> { }
what about Sync?
??? impl<F, T, U: Transmute<T>> Transmute<Lazy<F, U>> for Lazy<F, T> { }
pub fn lazy<T, F: FnOnce() -> T>(closure: F) -> Lazy<F, T> {
Lazy {
eval: eval_force::<T, F>,
data: unsafe { transmute(closure) }
}
}
pub fn eval<T>(self: &mut Lazy<T>) -> &mut T {
(self.eval)()
}
?? pub fn eval_share<T, F: Fn() -> T>(self: &Lazy<F, T>) -> &T;
pub fn unwrap<T>(self: Lazy<T>) -> T {
self.eval();
unsafe { transmute(self.data) }
}
fn eval_force<T, F: FnOnce() -> T>(self: &mut Lazy<F, T>) {
*self = Lazy {
eval: eval_noop::<F, T>,
data: unsafe { transmute(transmute::<_, F>(self.data)()) }
};
eval_noop(self)
}
fn eval_noop<F, T>(self: &mut Lazy<F, T>) -> &mut T {
unsafe { transmute(&mut self.data) }
}
impl Functor, Applicative, Monad, Comonad
impl Fn{,Mut,Once}?
return T: unwrap? Copy? Clone?
return &mut T?
or impl Deref{,Mut,Move}?
could impl `DerefMut`, but not `Deref`?!
QUESTION Lazy<F, T> or Lazy<T, F>?
* implicit existential quantification
* partial type application for Functor etc.
This seems to suggest Lazy<F, T>...
Should the Functor instance be forall F or exists F?
...certainly seems like exists would be correct!
In that case, does partial type application still matter?
Is there a relationship between p.t.a. and i.e.q.?
QUESTION when can we allow `eval()` through shared references?
I think GHC's blackholing corresponds directly to RefCell, and <<loop>> to a borrow failure
Can we avoid RefCell if we require Fn (pure) instead of FnOnce?
QUESTION (how) can we allow `eval()` in pure code?
By requiring `Fn` instead of `FnOnce`
Bifurcate into `Lazy` and `PureLazy`, or just require `Fn` at the `eval()` site (eval_share)?
Can we have an existential of `Lazy` produced by `lazy_pure()` where we know it supports `Fn`, without duplicating everything, and without carrying runtime evidence?
QUESTION how can we `eval()` from multiple threads?
By requiring Fn?
By using some thread-safe version of RefCell? (look into how blackholing works!)
(last three questions are closely related)
## UnsafeUnion<A, B>
sizeof(UnsafeUnion<A, B>) = max(sizeof(A), sizeof(B))
alignof(UnsafeUnion<A, B>) = lcm(alignof(A), alignof(B))
unsafe_transmute
A -> UnsafeUnion<A, B>
B -> UnsafeUnion<A, B>
UnsafeUnion<A, B> -> A
UnsafeUnion<A, B> -> B
if {x, y}: UnsafeUnion<A, B>, *x = y is unsafe!! destructors don't run
should it be a linear type??
impl<A: Copy, B: Copy> Copy for UnsafeUnion<A, B>
-- or just impl Copy for UnsafeUnion<A, B>?? or a disjunction??
likewise Share, Send, etc.
UnsafeUnion<_, _> is idempotent, commutative, associative
so we can just define UnsafeUnion3<A, B, C> = UnsafeUnion<A, UnsafeUnion<B, C>>
this is where constructor = transmute, instead of plain fn, would be important
with type macros perhaps UnsafeUnion!(A, B, C, D)
## TYING THE KNOT
fn tie<T>(build: |Gc<T>| -> T) -> Gc<T>
{
}
struct Stream<T> { head: T, tail: Gc<Stream<T>> }
fn repeat<T>(val: T) -> Gc<Stream<T>>
{
tie(|tail| Stream { head: val, tail: tail } /* tail not yet initialized here!! :( */)
}
use Box<Lazy<T>>? (Rc/Gc+RefCell?)
use a type-based HKT approach?
instead of `Gc`, parameterize it, is one opaque type while constructing/tying, becomes Gc once knot is tied
feel like `&out` should be involved somewhere...
should `tie` be a trait method? recursive structures make sense for `Gc` but not `Box` or many others
could we provide one for `Rc` and automatically use `Weak` in the right places?
## Trait names
// Semigroup
trait Combine<T> {
fn combine(a: T, b: T) -> T;
}
// Monoid
trait CombineWithUnit<T: Combine> { // ??
fn unit() -> T;
}
// Semigroupoid
trait Compose<Cat> {
fn compose<A, B, C>(a: Cat(A) -> B, b: Cat(B) -> C) -> Cat(A) -> C;
}
// Category
trait ComposeWithIdentity<Cat: Compose> { // ??
fn identity<A>() -> Cat(A) -> A;
}
// Group? Groupoid?
// Functor
trait Map<F> { // abstract over fn type?? e.g. `Option` would like `FnOnce`
fn map<A, B>(a: F<A>, f: &Fn(A) -> B) -> F<B>;
}
// additional reason to avoid `Functor` name: different meaning in C++ & others
// Contrafunctor
trait ReverseMap<F> {
fn reverse_map<A, B>(a: F<A>, f: &Fn(B) -> A) -> F<B>;
}
// InvFunctor, Bifunctor, Profunctor, Strong, Choice?
// Applicative
trait Apply<F: Map> { // Sequence? Map2? Multimap? ...
fn wrap<A>(a: A) -> F<A>;
fn unit() -> F<()>;
fn map2<A, B, C>(a: F<A>, b: F<B>, f: &Fn(A, B) -> C) -> F<C>;
fn pair<A, B>(p: (F<A>, F<B>)) -> F<(A, B)>;
fn apply<A, B>(f: F<&Fn(A) -> B>, a: F<A>) -> F<B>;
}
// Monad
trait Flatten<F: Apply> {
fn flatten<T>(a: F<F<T>>) -> F<T>;
fn and_then
}
// Comonad
trait Unflatten<F: Map> {
fn unwrap<T>(a: F<T>) -> T;
fn unflatten<T>(a: F<T>) -> F<F<T>>;
}
// Foldable
trait Fold<C> { // Summarize? Digest? Reduce? Process? Condense?
fn fold<A: CombineWithUnit>(a: C<A>) -> A;
fn foldMap<A>(a: C<A>, map: &Cn(A) -> B, combine: &Cn(B, B) -> B) -> B;
}
// Traversable
trait Traverse<C: Map + Fold> { // ??
fn sequence<T, F: Apply>(a: C<F<T>>) -> F<C<T>>;
fn traverse
}
// use "transform" for something?
## Mass borrowing with Transmute
//impl<'s, T> Transmute<&'s R<&'s T>> for &'s R<Box<T>> { }
//impl<'s, T, type<covariant type> R> Transmute<&'s mut R<&'s mut T>> for &'s mut R<Box<T>> { }
//impl<'s, 't, T, type<covariant type> R> Transmute<&'s R<&'t T>> for &'s R<&'t mut T> { }
impl<'s, T, type<type> R>
where for<A, B: Transmute<A>> R<B>: Transmute<R<A>>
Transmute<&'s R<&'s T>> for &'s R<Box<T>> { }
class SkolemA;
class SkolemB { x: SkolemA }
impl Transmute<SkolemA> for SkolemB { }
impl<'s, T, type<type> R>
where R<SkolemB>: Transmute<R<SkolemA>>
Transmute<&'s R<&'s T>> for &'s R<Box<T>> { }
## MISC QUESTIONS
- static stack space analysis + tables-next-to-code?
put stack space usage at a fixed offset next to fn body
if you get an fn pointer, can take the offset and find out
gets more interesting with HOFs
instead of a uint, store an fn pointer which takes as arguments the fn args to the HOF,
(or their stack usage, as uints), and calculates the stack usage of the HOF with those args?
but this stack-usage-calculating-fn would itself use stack?! infinite regress?
no: if it takes uints as args, then it's not a HOF itself
and/or maybe static bound can be shown globally for all such space-calculating fns (probs only do basic arithmetic...)
reminds me of type classes & dictionary passing... static polymorphism vs. dynamic
also of recursion schemes (f-algebras, catamorphism)
gets messier if the higher-order arguments aren't plain fns, but data structures of fns
in that case the stack-calculator would take the same datastructure with all the fn nodes replaced by their stack usage
this sounds highly nontrivial.
- no runtime overhead when only calling statically known fns
- strictly more expressive than "can only call static fns" formulation
- no restrictions on expressiveness? would probs still have to ban recursion (except tail recursion?)
- runtime overhead only where code would otherwise be illegal under only-static-calls
is this any better than just doing a stack check in fn prologues?
- "optimize out" references to zero-sized structs?
- can't literally make the references themselves zero-sized, but...
- it's guaranteed that no actual data will ever be read from them
- ditto for references to uninhabited types?
- therefore they can just be treated as `undef`
- moved to the end of function argument lists and not actually passed in
- moved to the end of structs and not considered for `sizeof`?
- is there anywhere this evilness is observable?
- would anyone care if sizeof((A, B)) >= sizeof(A) + sizeof(B) breaks?
- what about `ptr_eq`?
- unsafe code transmuting the type there and back would expect the reference to stay valid
- e.g. with pointers to opaque types in FFI
- would they ever use `&mut Thing` or `&Thing`, or just `*Thing`?
- probably would... ?
- perhaps only do this for _either_ zero-sized or uninhabited references, but not both?
- &Void and &mut Void are inherently unsound!
- or more like: only for `data struct`s? (and `data enum`s?)
- then I think we only have the `ptr_eq` problem
- can stack maps / stack tracing also be used for relocatable stacks? (why not?)
have a map of all non-heap refs/pointers on the stack
what about *T?!
any of these which point to an address inside the stack are incremented with the offset to the new stack
- what should a not-fully-general Drop or Trace impl mean?
A: not-covered types should have glue as normal, just not the impl
B: a not-covered type is illegal
given A, then B can be expressed with a smart constructor
also allow trait bounds on struct typarams?
only covers the case where Drop is restricted to a trait, not to a type (requires an (~) constraint)
think this should be a separate feature
- is there any way to give a safe type signature to `alloca`, with e.g. &out?
(obviously needs to be some kind of built-in intrinstic, can't be an actual function)
(only makes sense for dynamic things, if you know the type/size at compile time you can just `let`)
fn alloca<T>(n: uint) -> &out [T];
fn alloca<ref T>() -> &out T;
but lifetime needs to be tied to current stack frame somehow...
and can _only_ work for the current stack frame/lifetime
can't alloca into caller stack frames, and callee stack frames don't even exist
so passing in the lifetime as a parameter cannot be part of the solution
add a magical lifetime which always refers to the lifetime of the current scope?
fn alloca<T>(n: uint) -> &'self out [T];
but the `'self` lifetime of the caller isn't in scope at the type sig of `alloca`...
maybe trying to write it as a normal fn type signature is a bad idea in the first place,
& it should be a built-in operator instead...
like you can't write the type signatures of the `&` and `&mut` operators in the language itself either
but then specifying the argument & return without overspecializing becomes the problem
surely fixed-length arrays and `ref T` aren't the only meaningful possibilities
what's the most general formulation? is there one?
is it actually `ref T`? does the fixed-length array case fit into it?
so conceptually: `alloca: ref T -> &'self out T`?
should this even be exposed as a separate "thing"?
maybe the right thing to do is to just allow using `ref` types in `let`s (which would otherwise be strictly forbidden as not representable),
and use alloca under the hood?
...would this actually allow removing all restrictions on use of `ref` types in fn bodies - and thus the need for `ref` itself???
does this end up being intensional type analysis at the limit??? wouldn't be surprised...
- `let x = ...; let y = &move x; x = *y;` seems like it should be OK? saying "actually, don't destroy it after all"
can this be shown safe in general?
is there an analogue for `&out`?
`let x; let y = &out x; ???`?
- how to relate `trait Foo<T>` + `impl<T> Foo<T> for My<T>` and `trait Foo for type<type> Self` + `impl Foo for My`?
trait GBox for type<type> Self where for<T> Box<Self<T>> ???
class forall a. Box (b a) => GBox b
- how to specify subtyping nicely?
http://www.reddit.com/r/haskell/comments/1u9pbw/feedback_sought_a_typetheoretic_account_of/ceg4cos
- how to do TCE?
TCE calls are always explicit
if moves/drops are tracked statically, and TCE is explicit, those are no problem
static error in case of conflict
problem: calling conventions
C convention: caller cleanup
we need: callee cleanup
QUESTION where do we need it?
"The problem seems to be that you can't do tail calls with the C calling convention.
For example, let's say a 0-argument function tail calls a 1-argument one: the 1-argument function expects an argument on the stack, so the 0-argument function must push one.
However, when the 1-argument function returns, the argument will still be on the stack, because with the C calling convention the caller removes arguments from the stack.
But the caller called a 0-argument function, so he won't remove any argument, and thus the stack is now misaligned due to the argument left over there, which will crash the program soon.
However, just switching to the Pascal/stdcall convention, where the callee removes arguments from the stack should just work; it might be slightly slower, but on all modern architectures (i.e. those that aren't x86-32) parameters are going to be passed in registers anyway for must functions, so it shouldn't matter.
The problem of that is that non-varags functions cannot be called with a varargs prototype; this is an issue with K&R C that allows calling undeclared functions, but isn't an issue with Rust.
So, I'm not quite sure why Rust doesn't just switch calling conventions and support tail calls."
solution:
make a callee-cleanup wrapper around every fn?
always at a fixed offset
should always be 2 insns: call f; ret N;
TCE calls take the offset before calling
but it's the last call that needs to be caller-cleanup, not the first...?
more precisely:
f calls g, g tail-calls h, then:
f (not g) needs to know that g will do TCE?
h (not g) needs to know that g will do TCE?
or alternately:
don't actually do tail calls, just re-use own stack for arguments of callee
not complete because return pointer is still added to stack
what should the keyword be? go? in? out? tail? goto? self? use?
"Tail calls can be made explicitly in Perl, with a variant of the "goto" statement that takes a function name: goto &NAME;"
http://perldoc.perl.org/functions/goto.html
interacts how with explicit/implicit return?
`return` is always tail unless followed by drops?
- how to do variadic generics?
C++ solution is too "big" and complex
primary use case: abstracting over # of fn parameters
but also need to 'splice', e.g. for `trait Fn`
also would be nice: type c_union<type..>
how might Applicative look?
interaction with HKT?
is a variadic parameter its own kind?
interaction with type inference?
is everything either a map or a fold? do we need explicit recursion?
what contexts are maps, folds, splices allowed in?
which parts of C++11 variadics does Rust need, which parts are handled in other ways, which parts are out of scope?
do we only need it for types, or also non-types and HKTs?
is providing variadic args implicit like C++, or explicit? e.g. c_union<[int, char]>
have explicit syntax, allow sugar when it's unambiguous?
but that's essentially tuples... would deconstructing, mapping, and splicing tuples be enough?
USE CASES:
trait Fn, FnMut, etc.