Skip to content

Instantly share code, notes, and snippets.

@agalatan
Last active June 29, 2022 22:08
Show Gist options
  • Save agalatan/f1d2530ed3df54e7cb3c6805d9dbe758 to your computer and use it in GitHub Desktop.
Save agalatan/f1d2530ed3df54e7cb3c6805d9dbe758 to your computer and use it in GitHub Desktop.
[Flow] Conditional types
// 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
@agalatan
Copy link
Author

agalatan commented Jul 16, 2019

                                       $Match - $Case - $Default
                      to be used in a very similar way to Scala's matching
                 
                                                                                     in memoriam Macaw-Swift
                                                                                "you were good and taught us well"
                                                                                         Rust in Pieces

$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:

  • The predicate is tested against original input, given to $Match, and not against (the weaker) C
  • This is similar to Scala, where the @ is used to "bind" and reuse the original variable in a Case

NICE TO HAVE, BUT I DON'T KNOW HOW TO DO IT:

  • Ideally, we'd write the $Case like in Scala: $Case<C> => R or $Case<C, P> => R
  • Which would look in the end like $Match<X, $Case<C1, P1> => R1, $Case<C2> => R2>
  • Help needed to achieve this ^^

Final product looks like below. See concrete examples at the very bottom of the Flow file

$Match<                
    X,                  
    $Case<C1, P1, R1>, 
    $Case<C2, R2>,     
    $Case<C3, R3>,      
    $Default<R4>       
  >

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment