Last active
June 29, 2022 22:08
-
-
Save agalatan/f1d2530ed3df54e7cb3c6805d9dbe758 to your computer and use it in GitHub Desktop.
[Flow] Conditional types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See the flow link at the bottom, and the GIT comment below | |
// Good utils | |
type $If<X: boolean, Then, Else = empty> = $Call<((true, Then, Else) => Then) & ((false, Then, Else) => Else), X, Then, Else>; | |
type $Not<X: boolean> = $If<X, false, true>; | |
type $And<X: boolean, Y: boolean> = $If<X, Y, false>; | |
type $Or<X: boolean, Y: boolean> = $If<X, true, Y>; | |
// Unique, internal Symbols. Probably I can actually use JS's Symbol. Need to see how well Flow implements it | |
// They match nothing else, nowhere else, never ever. | |
declare opaque type __PLACEHOLDER1; | |
declare opaque type __PLACEHOLDER2; | |
declare opaque type __PLACEHOLDER3; | |
type __PLACEHOLDER = __PLACEHOLDER1 | __PLACEHOLDER2 | __PLACEHOLDER3; | |
// First two have fallback (aka `else`, aka `default`, aka `case _`) | |
// while the third does not: it "throws" if it does not match | |
type __IfPlaceholderThenElse<X, Then, Else = empty> = $Call<(__PLACEHOLDER => Then) & (mixed => Else), X>; | |
type __IfNotPlaceholderThenItselfElse<X, Else = empty> = $Call<(__PLACEHOLDER => Else) & (mixed => X), X>; | |
type __IfNotPlaceholderThen<X, Then> = $Call<true => Then, $Call<(__PLACEHOLDER => false) & (mixed => true), X>>; | |
// The regular $Case, but it always has a predicate P | |
// i.e. if $Case does not have predicate, then consider the predicate to be () => true | |
type __CASE<C, P, R> = <X>(X) => $Call<((C, true) => R) & (mixed => __PLACEHOLDER3), X, $Call<P, X>>; | |
/* | |
If both "A1" and "A2" are specified, then: | |
"A1" is a predicate used to test the matched input against | |
"A2" is the return type of the $Case, hence of the $Match | |
If just "A1" is specified then | |
There is no explicit predicate, so we'll consider it to be () => true | |
"A1" is the return type of the $Case, hence of the $Match | |
*/ | |
type $Case<C, A1, A2 = __PLACEHOLDER1> = | |
__IfPlaceholderThenElse< | |
A2, | |
__CASE<C, () => true, A1>, | |
__CASE<C, A1, A2> | |
> | |
// Defining $Default as $Case<any, R> also works, but better be explicit and redundant here | |
type $Default<R> = $Case<any, () => true, R>; | |
// Unfortunately, there is a maximal number of $Case that can be used | |
// But just list as many as you want (20, or 100) and CPU & RAM are the only upper bound | |
// These have to be written "anyway" by hand in the codebase that uses them. | |
// Currently 5. If first one does not match, then we shift until there's nothing left to shift | |
type $Match<X, CPR1, CPR2 = __PLACEHOLDER2, CPR3 = __PLACEHOLDER2, CPR4 = __PLACEHOLDER2, CPR5 = __PLACEHOLDER2> = | |
__IfNotPlaceholderThenItselfElse< | |
$Call<CPR1, X>, | |
__IfNotPlaceholderThen< | |
CPR2, | |
$Match<X, CPR2, CPR3, CPR4> | |
> | |
> | |
('yeey': $Match<10, $Case<number, 'yeey'>, $Case<string, 'poop'>>); | |
('poop': $Match<'caca', $Case<number, 'yeey'>, $Case<string, 'poop'>>); | |
(101: $Match<null, $Case<void, 'yeey'>, $Case<string, 'poop'>, $Default<101>>); | |
// Uncomment to see expected error | |
// (1001: $Match<10, $Case<number, 'yeey'>, $Case<string, 'poop'>>); | |
// (true: $Match<10, $Case<number, 'yeey'>, $Case<string, 'poop'>>); | |
// ('yeey': $Match<{ x: true }, $Case<string, 'yeey'>, $Case<number, 'poop'>>); | |
// (101: $Match<null, $Case<void, 'yeey'>, $Case<string, 'poop'>, $Default<102>>); | |
// With predicate as a second argument of $Case | |
('yeey': $Match<{ x: true }, | |
$Case<{}, ({ x: true }) => true, 'yeey'>, | |
$Case<string, 'poop'> | |
>); | |
('A cat that meows. Must be my cat': $Match<{ says: 'meow', type: 'cat' }, | |
$Case<{ says: 'meow' }, ({ type: 'dog' }) => false, 'A dog that meows. Neither my dog, nor my cat'>, | |
$Case<{ says: 'meow' }, ({ type: 'cat' }) => true, 'A cat that meows. Must be my cat'> | |
>); | |
// Uncomment to see expected error | |
/* | |
('yeey': $Match<{ x: true }, | |
$Case<{}, ({ x: false }) => true, 'yeey'>, | |
$Case<string, 'poop'> | |
>); | |
*/ | |
// https://flow.org/try/#0PQKgUAkAfjtzFEQOoHsBOBrABASwHbYAO6qA5ugKYDO12AFACarb6oAu276ArtZwE9K7AJRJo8SeOyRxSbAoUASALIBDdgGMAFtgC02JQGE11SvsMARSgDM1PADbswi12-dv2LAEbm+lRjxCNWwAN0p0AWxqXABbXAc1dGwAdzUor2wAZU01RIByOliNHQIyFw9Kqoqq2tc5cQJsWMpYjFw1WOx1XJS9LJTcG2c60bHx9wAiAVQeVIjzMlRUQLV8QPZ7Mm1OPnmHB0maiZPTyohXACU+TiaABVxKTRpjs7f3uuOIY1NzKmfcOE6JRcOxtBFsAAmbAYbAAZmwSTIPBa+HY1AAXAwTGYjIlaAAabB3KiMXC5diUImXYQ8dD4ERfb44ygAHiM1IAfNgIC01nRJkMGCEyIDKIQCEQeJwABp4OioGxcAREcxGERccGES5HUY-Mzsol3LkKPn4AVC+gisUS-BS2XymFK9gqtUagCCADlLAw5dQNLhqDZHnQweYSAFyRpzHcNWHtbqPBBXh4AJIqO4AeUuABUvTmsUyc+DiKSo5THZT+AFEWQ1AR+DD0LhRfg8kF7UTReFCJlVCVtES1oE2Jw1HWG5x6GH5mpMBENUYmVk4gkklwWDk8moiSlwVRNeYAAKO-wbFiTbwERiTRHrbBUfyHpstgjt0JJDreBzmJohFkpu4gFuJ6qZGAAotgOaZtgAAS7oAGrgUSABCACqObYKm2CWJmnr5JhADSnqZsgcGkVBMG4VhBbAa4qaMJQeQOAIu6UPkgQpM2FYzvq5gOLg85BNkuSJFiAAGfHstyAC83KXOJTbYJJLKGsSsnyeJdGKHu5K6A4yyYHQTQzuKgQCUJknqFo2isjKRJSUYACMRpORp2CXC5hiqUYkLuZcflaaMsGUA4RCsJQAQ1pkailJQ4SaoG2AAHrJYByaVAAYm+DilisPCaJwBmoEZ2AWeYvgGSkAB02SRdgmioPgmhUBWlAAB6dEQP50Boz7hJE2DeBwXixNVwH9jZrInEy9lnMBzK-OyXl3F5nmckSC2OZC1J+USrhbT5cLUnCG1AecSjWHYjjsKylwACzcudigQJyYAgMAYDAMA2AAOLLIE0oJNQYAuqqhipjYdlYsNqA-msRLFuKRLgQ4ZjYDJ2CtEQLqyd5zGsvQ068FSUFaijaOUBqclk+KGoAGQMPQdiU4j5PYKjZjU9ynNU0Sc1I-gFNmJyADcYCg66hiehw0NDcs8P4HjSiQ3ZRIs2YRLcDwlBi5L4NKO66xy7DitEgAmjDCtMUrGMQ1Dc3m+reQi+LYPmEomboCb1sI9glvy3DNvK6rc3a6T5t619P1ofguAAI460SBCUvS7ZZAIsTDWjtUkqg3hqN+UTYbkwSFTwzFRE+ABSWSFNkmfZ7VnqRee0T1doqApPsuWZVVeCxN1rTiuieDON9tNRMUNmsBw2hlFjrOz3uCyL5rEUDVjA3jYxmiJAeqBEGoifmO72AAPrn3cAAy7oQbBmbX5Y4GeeLu-7+Yh-Hzryrg5fN933Ag-J+L9IRvyeB-GER8T6-3MP-W+99H7P0uHCN2Ut4GAOAcgu2GDEEgM8tgAAPhfK+CCgFINAUQkhAC8HINQRLCe2V0CNnYCkFg2g1AJRZg4AumgcBWkwCEcSoUzDiSHII5SjFrpODEYiCR4lcjo3PuJRkE9dI-mfGCXA6BAjMBoLPdgWJQTYEmGCUgKRqC3iFMYvRdBRzNAHPrOB59IZ3ESM8TuDhGLoEFrzNWtMhYc0pnbbGuM7Y-AOITXB5D8EY25ILBmDB4jtRrDTXmIh+Z6zPpfSGMt2BuNipQTx3jBapnRKFGwfi5q8xCYPMJmMIkOCiaQzBFDLhxKCVzbAjN6DJNSdyGUGTsAyiyeglxNg8kFI8XDEpWp-GC2ViYSJ4cOmCwckspp9BolYJfh0jWVNulJNwCkwINNw5DJGVHCeSMHyUGRPvfG69vDSjHoiBwaQBB0A4b1UskYKQxmjngaqlBapCj4tgWxBjsAcIShGMk-ytZagak1GI3jnxwvLKfHw5h6Dcy4CTJxJCjDuiyOBNSxoPJ4zspyeggyOmNMJvQDk+KdZ4suIk3pxz+nULITslBFz1kEwpZc8WX1wD0SVMNMEJj3ROVvMOGVkJ5UHmoKqTQQxHiMEReKDEXxJiyqsT8jF-zsBng3FwGgnAZzTx0DWSULzxz1nNCMF6+qlWVhLK1OkvYpaKmfHxIkWpnhOn9dZHQkBIbYAAFY3BlXKx0qqngauilqL4SMDxJTYFjdq3VyTGONdGIk1AWApHYgcZF5pcBouMZkXwDA8Xhz1Qaj1fxaT0lgSG3iLJA3imDX63iYbtDvU+mfRyRJZXjuhJjbZbS3IYwqDkmwUyikzIiL4ymrJjjuh2jIRQl9iWkrUrijp4dx1uQJDUfdJKyXMondgbdb03pAQnldAgC9Lq2HsE4REdApJrFYpSt5xbUgYCMkSZ5nBfDsFTkNcwHVc3qrHPeUkPB1hrE4PuSghKP3SNupcRZS1-1EmPWckmXJRUT1jjYDA7BUPRhYtqjNPzijtTiO2fAKJfDJD9eCsEfVS6wdNWYRggKUIvJjY2ASjZTAOPwFEGTMw5hpDRAwSEAAGIksInJqbUxqBVRg7hoUOZcd0KhEQHhnE1FipqiCqmSMNVDInrngnRjCrFgmuKgkpIQSY-6Pm3m8FEDh94TIlkaoxAu6M+O7DMKGcEY1AVGDpFQNE1mACstVI3BmYZwJq5hIX2JtYOQ8hBS3RHnsMU1aIEiHioPXUc898BkDKrYK1LBqAVZdQdUdg7-EGc8kSfrU6eWtPwTu-rCJp0tNoaAwbdwHo4OmzE5B435tpcWzQ5boDZILvGZM9xK6vFrq1GUswDhKkboqAy-rXkRkXoUIu-bhTinHfFOyebO7Jo6D6x9ubKC-uPUfRLeg+QhCUAEPkLEX3bLacFQaDjWcIhElB5FCHZ0pL8GbE15HRBlhEHyJyTkIhxYg9x4fSHhhev5FyLkfIcO2QI648jsHaP6eskx2UHHeOCdE5J9ppyUPescYOGz0IqAq3M9RwTtnHPsfYHyGT-H6OrpftuvzwnxOGEx2aqgWIqI2vtzgzmp4lJAgRFIOgQF9BtNqYF5TgcrJYePIZ5xpH8uWfS+d+z7gnP5eK555riexMdaC4d07qSjO3co-B57jHPu5cK+5xr8WQfo8Q9D1NAA3tgdqWIVkAF8Zfx7IJLmP6PVKR-QFz8nyerf84z994XDhRfi61e7qX5elqy5L37pPDkVc3Ud2pvyvOtfYGQKCXQBaKwyZCGYRq94kQohHiGviYAQce4b7ZbPueWXmEL1d1SmfC8MB33nkm2B88NrI+3sv92vfd+r0rsAvPKjaRmiD90DU+rReaJQLu1AtUKgsadasQUQFIFO0OrI2e-onyWI+QLQXcdOsC8BEBl+9+UkMB6QmI8uiBKQ+Q6Bp+KB8uzAZABBV+eyLspM+QX+pBmofUeBgB2ALck+EIYBEK5ARIbAyQ7BEBG0h+S0WBcBuB-++BhB9A2e7sqBGg5B1+Sc8uX+FI9BnAjBQBIB5gvBMhj6xOHwQEgKscjUeuK+mQZgRuaqpuWM6AFuowYqMgG+UuW+0BOe5+P8B+ygR+J+Ehzh2A+yl+ch1BHu-B7hXexeT+BOL+muH0uh7gQAA |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
$Case receives either 2 or 3 arguments: (CaseClass, Predicate, Return)
$Case<C, R> = if (a given input X is of type C) then R
$Case<C, P, R> = if (a given input X is of type C) AND (X satisfies the predicate P) then R
IMPORTANT:
NICE TO HAVE, BUT I DON'T KNOW HOW TO DO IT:
$Case<C> => R
or$Case<C, P> => R
$Match<X, $Case<C1, P1> => R1, $Case<C2> => R2>
Final product looks like below. See concrete examples at the very bottom of the Flow file