unisonweb / base_v1

Unison base libraries, published using V1 codebase format
24 stars 14 forks source link

Add a whole bunch of functionality to base #4

Closed runarorama closed 4 years ago

runarorama commented 4 years ago

This also changes the type of List.init to be in line with List.head, List.tail, etc.

The changes summarized below are available for you to review, using the following command:

pull-request.load https://github.com/unisonweb/base https://github.com/runarorama/base

Updates:

 List.init : [a] -> [a]
 ↓
 List.init : [a] -> Optional [a]
 +  unisoncomputing2020 : License
 +  dolio               : Author

 List.powerslice : [a] -> [[a]]
 ↓
 List.powerslice : [a] -> [[a]]
 +  unisoncomputing2020 : License
 +  dolio               : Author

 Nat.drop : Nat -> Nat -> Nat
 +  Nat.drop.doc : Doc

Added definitions:

 Char.toText                           : Char -> Text
 Float.pi                              : Float
 ┌ Int.^                               : Int -> Nat -> Int
 └ Int.pow                             : Int -> Nat -> Int
 Int.abs                               : Int -> Nat
 Int.decrement                         : Nat -> Int
 List.adjacentPairs                    : [a] -> [(a, a)]
 List.all                              : (a ->{e} Boolean)
                                       -> [a]
                                       ->{e} Boolean
 List.any                              : (a ->{e} Boolean)
                                       -> [a]
                                       ->{e} Boolean
 List.break                            : (a ->{e} Boolean)
                                       -> [a]
                                       ->{e} ([a], [a]) (+1 metadata)
 List.chunk                            : Nat -> [a] -> [[a]]
 List.chunksOf                         : Nat -> [a] -> [[a]]
 List.concatenate                      : [[a]] -> [a]
 List.dropRight                        : Nat -> [a] -> [a] (+2 metadata)
 List.dropRight.doc                    : Doc (+1 metadata)
 List.dropRight.examples.ex1           : [Nat] (+1 metadata)
 List.dropRight.test                   : [Result] (+2 metadata)
 List.dropWhile                        : (a ->{e} Boolean) -> [a] ->{e} [a]
 List.filter                           : (a ->{e} Boolean) -> [a] ->{e} [a]
 List.filter.tests.constFalseIsEmpty   : [Result] (+2 metadata)
 List.filter.tests.constTrueIsIdentity : [Result] (+2 metadata)
 ┌ List.first                          : [a] -> Optional a
 └ List.head                           : [a] -> Optional a
 List.head.tests.headOfCons            : [Result] (+1 metadata)
 List.head.tests.headOfEmpty           : [Result] (+1 metadata)
 List.init.tests.initOfEmpty           : [Result] (+1 metadata)
 List.init.tests.initOfSnoc            : [Result] (+1 metadata)
 List.intercalate                      : [a] -> [[a]] -> [a]
 List.intersect                        : [a] -> [a] -> [a] (+1 metadata)
 List.intersect.tests.spec             : [Result] (+2 metadata)
 List.intersectBy                      : (a ->{e} a ->{e} Boolean)
                                       -> [a]
                                       -> [a]
                                       ->{e} [a]
 List.intersperse                      : a -> [a] -> [a]
 List.isEmpty                          : [a] -> Boolean
 List.isEmpty.tests.consIsNonempty     : [Result] (+1 metadata)
 List.isEmpty.tests.emptyIsEmpty       : [Result] (+1 metadata)
 List.iterate                          : Nat -> (a ->{𝕖} a) -> a ->{𝕖} [a]
 List.iterateUntil                     : (a ->{𝕖} Boolean)
                                       -> (a ->{𝕖} a)
                                       -> a
                                       ->{𝕖} [a]
 List.iterateWhile                     : (a ->{𝕖} Boolean)
                                       -> (a ->{𝕖} a)
                                       -> a
                                       ->{𝕖} [a]
 List.last                             : [a] -> Optional a
 List.last.tests.lastOfEmpty           : [Result] (+1 metadata)
 List.last.tests.lastOfSnoc            : [Result] (+1 metadata)
 List.minus                            : [a] -> [a] -> [a] (+1 metadata)
 List.replicate                        : Nat -> a -> [a]
 List.replicate.tests.length           : [Result] (+1 metadata)
 List.scanLeft                         : (b -> a ->{e} b)
                                       -> b
                                       -> [a]
                                       ->{e} [b]
 List.scanRight                        : (a -> b ->{e} b)
                                       -> b
                                       -> [a]
                                       ->{e} [b]
 List.span                             : (a ->{e} Boolean)
                                       -> [a]
                                       ->{e} ([a], [a])
 List.splitAt                          : Nat -> [a] -> ([a], [a])
 List.tail                             : [a] -> Optional [a]
 List.tail.tests.tailOfCons            : [Result] (+1 metadata)
 List.tail.tests.tailOfEmpty           : [Result] (+1 metadata)
 List.takeWhile                        : (a ->{𝕖} Boolean) -> [a] ->{𝕖} [a]
 List.tests.gens.nonEmpty              : '{Gen} [Nat]
 ┌ Nat.^                               : Nat -> Nat -> Nat
 └ Nat.pow                             : Nat -> Nat -> Nat
 Nat.decrement                         : Nat -> Nat
 Nat.drop.doc                          : Doc
 Nat.drop.examples.ex1                 : Nat
 Nat.drop.examples.ex2                 : Nat
 Nat.pow.tests.expansion               : [Result] (+2 metadata)
 Nat.pow.tests.homomorphism            : [Result] (+1 metadata)
 Nat.tests.gens.smallNat               : '{Gen} Nat
 Nat.times                             : Nat -> (a ->{e} a) -> a ->{e} a
 Optional.join                         : Optional (Optional a) -> Optional a
 Optional.toList                       : Optional a -> [a]
 Search.findIndex                      : (a ->{e} Boolean)
                                       -> [a]
                                       ->{e} Optional Nat
 Search.findLastIndex                  : (a ->{𝕖} Boolean)
                                       -> [a]
                                       ->{𝕖} Optional Nat
 Text.chunksOf                         : Nat -> Text -> [Text]
 Text.cons                             : Char -> Text -> Text
 Text.fromBytes                        : Bytes -> Text
 Text.join                             : Text -> [Text] -> Text
 Text.length                           : Text -> Nat
 Text.snoc                             : Text -> Char -> Text
 Text.split                            : Text -> Text -> [Text]
 Text.startsWith                       : Text -> Text -> Boolean
 Text.toBytes                          : Text -> Bytes
 Tuple.curry                           : ((a, b) ->{𝕖} c) -> a -> b ->{𝕖} c
 Tuple.uncurry                         : (a ->{𝕖} b ->{𝕖} c) -> (a, b) ->{𝕖} c
 Universal.!=                          : a -> a -> Boolean
 Universal.max                         : a -> a -> a
 Universal.min                         : a -> a -> a
 asTypeOf                              : a -> a -> a
 bind                                  : (a ->{𝕖} b ->{𝕖} c)
                                       -> (b ->{𝕖} a)
                                       -> b
                                       ->{𝕖} c
 contains                              : [a] -> a -> Boolean (+1 metadata)
 flip                                  : (a ->{𝕖} b ->{𝕖} c) -> b -> a ->{𝕖} c
 fuse                                  : (r ->{𝕖} a ->{𝕖} b)
                                       -> (r ->{𝕖} a)
                                       -> r
                                       ->{𝕖} b
 until                                 : (a ->{e} Boolean)
                                       -> (a ->{e} a)
                                       -> a
                                       ->{e} a
 while                                 : (a ->{e} Boolean)
                                       -> (a ->{e} a)
                                       -> a
                                       ->{e} a
 xor                                   : Boolean -> Boolean -> Boolean

 patch List.patch (added 1 updates)
 patch patch (added 27 updates)
pchiusano commented 4 years ago

@runarorama cool!!! Can you adjust this to go with the conventions described here on where to put tests, etc. Plays nicer with suffix-based name resolution.

Sorry will be a little tedious to update. :|

runarorama commented 4 years ago

I think this now conforms to the conventions.

runarorama commented 4 years ago

Removed some controversial aliases

pchiusano commented 4 years ago

@runarorama okay, reviewed this some more. I realized some neato stuff!

In general, we shouldn't use patches / updates unless the new definition is basically always to be preferred to the old. The old versions of init and tail are perfectly cromulent functions that aren't buggy or anything, it's just that we decided we liked those names being used to refer to different definitions. All good. But just because we think those names are better suited for different defs doesn't mean we want anyone using the existing defs to go through a refactoring session. If Alice has written code using the old init and tail, do we want to force her to upgrade when she gets the latest base and applies the patch to her code? No not really.

That is, other languages force upgrades on people since any name change is a forced upgrade. For Unison, the choice of doing a name change is separate from the choice of whether the user should have to do any work to upgrade.

To reassign existing names to new definitions without adding to the patch, you would first do a rename.term (or a delete.term) on the old definitions (maybe drop1 and drop1End), then introduce the new definitions with the new type signatures and the names init and tail. Nothing is recorded in the patch then, and anyone upgrading their usage of base by applying the latest base patch to their code won't be affected, other than maybe seeing different names for definitions.

Cases where you'd maybe do an actual update vs just deleting or renaming the old one:

What I think is super cool about this is how it means less churn for users of the ecosystem. You aren't forced to deal with needless upgrade cycles just because the maintainers of libraries have evolving ideas about what to call things. :)

Add explicit ability annotations for now

Due to some ability inference limitations (this issue for example) functions like Nat.times get not great inferred types:

Nat.times : Nat ->{𝕖} (a ->{𝕖} a) ->{𝕖} a ->{𝕖} a

That should not need any abilities after the first or second arguments, but the signature as given means you can't even partially apply it and write times 4 inside a pure function. Pretty sure the implementation will typecheck if you gave it the more general signature Nat -> (a ->{e} a) -> a ->{e} a.

Can you go through all the ability functions and just add ability annotations manually to be exactly what you want and would expect, even when the function is partially applied?

Provide annotations to top level thunks

Due to this issue, you get a weird type for an inferred thunk:

Nat.tests.gens.smallNat               : ∀ (). () ->{Gen} Nat

This is just because 'a is literally parsed as _ -> a, with a variable name of (). We should fix that, but in meantime, I'd just add an explicit annotation to these as well ('{Gen} Nat should do oit) so we avoid weird looking types.

runarorama commented 4 years ago

How do I remove init from the patch?

runarorama commented 4 years ago

@pchiusano I believe I've addressed all the issues you raised.

runarorama commented 4 years ago

Is this OK to merge?

pchiusano commented 4 years ago

@runarorama sorry missed original notification, lemme take a look. Thanks for addressing.

I think I still want to hold off on merging more stuff to base until we get to the bottom of the extra namespace files. @aryairani

pchiusano commented 4 years ago

Okay, we discussed, and going to close and break this one up. Also it's affected by https://github.com/unisonweb/unison/issues/1381 so history should probably get tossed out and relevant definitions copied over.