Open NickSeagull opened 6 years ago
@NickSeagull Completely agree with almost all of this. If possible, can you start a proposal on eta-proposals where you describe all the functions that should be present in the prelude. Include open ended questions on the parts you're unsure about and we'll work it out over time.
The plan moving forward is that we'll have a new .eta
extension which automatically turns on a lot of Haskell's language extension that have proven to be useful defaults (documented here). If you send a .hs
file to the compiler (i.e. when compiling Hackage packages) it'll go back to GHC's defaults.
Love the fact that we will have our own extension! Will create the proposal, this deserves a thorough thought, though :smile:
I teach Haskell. I think this would cause problems with my team learning/using Eta and I don't think there's evidence that it would be useful.
I don't agree with this. There is an important order of arguments to (<$>)
and (<*>)
that makes one preferable over another (this is a rabbit hole). The name (<*>)
comes from the original paper, Applicative Programming with Effects. To flip the order of these arguments, is by definition, flipping the correct order of arguments. That is not to say this flipping is not useful, but that it is occurring.
read
should be derived from one direction of an IndexedPrism
. From this, the function String -> Maybe a
will fall out.
FYI, the type-class hierarchy in Purescript came mostly from Scalaz, which is still a WIP, though has mostly stabilised compared to what we had prior. Not sure if that might help.
Finally, I don't think "natural in java" is a good reason for anything. In the early 2000's, after I'd been working on the JVM, my colleagues were arguing with me over "what is natural" (a simple logical fallacy on its own), so I just wrote the code: http://functionaljava.org/ and asked, is this natural? None of them use Java anymore.
I think eta needs to be careful about changes that makes difficult to projects be compiled with different ffi backends (ghc/c, ghcjs/js, eta/jvm). So i would try to avoid default substitutions and deletions respect to the actual haskell standard prelude. And i would try to make additions (including alias) coherent with the actual constraints and naming conventions. Even with a different file extension i think it is important that you could be able to write standard haskell code in a .eta file and it should work without change the default config. What do you think about add the operators as alias of existing ones instead of replacing them?
I'm not talking about removing the actual prelude, but rather have a mandatory import Eta.Lang
with a NoImplicitPrelude
extension by default @jneira :smile:
Regular projects would still compile. Its the same as using Foundation or ClassyPrelude in a Haskell project
@tonymorris , didn't know about that PS was derived from Scalaz. Although I now refrain from removing stuff, I still suggest to have special operators. In the end the majority is the one who dictates the norm, and we should adapt to that in my opinion.
@NickSeagull I agree with @jneira that existing functions/names shouldn't be changed, but aliased when possible. And for you suggestion read :: String -> Maybe a
, we should probably name it readMaybe
instead to avoid changing the type of an existing function.
@puffnfresh There isn't any evidence, but the style of writing code (ordering) does matter. My personal thoughts on this are that when transitioning to functional programming, people already have to expend energy on learning the new abstractions (monads, etc.) which is worthwhile since this knowledge ends up being language-agnostic.
But I'm not sure trying to re-train their brain from fluent-style is a worthwhile expenditure since it's just about style and not about content. Of course, if there are benefits that are worth the expenditure that I'm not seeing right now, I'd be happy to listen.
If functional programming was widely taught in colleges, I would agree that it makes no sense to change the order from the "correct" one, but that is sadly not the case and won't be for a long time. Therefore, we pretty much have to assume everyone will have the imperative/OOP mindset starting out and work from there.
@tonymorris While I do agree that order of arguments affects how you can write compositional pipelines, can you provide concrete examples where @NickSeagull's suggested order proves to be inconvenient?
As discussed, we will not be mutating the existing Haskell prelude, but layering on top, so at the end of the day, you don't have to use the new aliases in your own application code, but we will most likely encourage usage of the new aliases for packages that are purely meant for Eta alone and in all Eta learning material.
As to your argument about "natural" I completely agree. But see the argument above about energy expenditure. Moreover, it's not just about Java - F#, Elm, and many other functional languages have followed that order to such an extent that even programmers with experience in these languages will sort of expect it.
Does a library like this already exist?
@puffnfresh library like which?
If the goal is "to do better than Haskell prelude", then
a) there is a huge opportunity here to do well b) there is a chance to screw it up, let's not!
@tonymorris I'd love to accomplish that goal, but also, on the other hand I'd love to see helpers for newcomers to Haskell, like @rahulmutt noted in his comment
@puffnfresh there is flow by Taylor Fausak, which is absolutely lovely, but I'd like to see more stuff included
I'm teaching in Melbourne next week, and Brisbane the week after. See @queenslandfplab on Twitter. You coming?
On 27 Jan. 2018 22:42, "Nikita Tchayka" notifications@github.com wrote:
@tonymorris https://github.com/tonymorris I'd love to accomplish that goal, but also, on the other hand I'd love to see helpers for newcomers to Haskell, like @rahulmutt https://github.com/rahulmutt noted in his comment
@puffnfresh https://github.com/puffnfresh there is flow https://github.com/tfausak/flow#readme by Taylor Fausak, which is absolutely lovely, but I'd like to see more stuff.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/typelead/eta/issues/617#issuecomment-360982403, or mute the thread https://github.com/notifications/unsubscribe-auth/AAM2fn2ag6tKazToz9tRlZg-oq_lAeGEks5tOxmxgaJpZM4RmdZb .
I think this discussion should also include how we're going to deal with lenses. Lenses are like the jQuery for pure FP languages and as such, I think it's rather unfortunate that they don't have support by default in the compiler (via deriving Lens
) and the standard library (core lens type synonyms).
I've been going through the internals of the lens
package in my spare time to see if we can extract a simple core that is powerful enough to handle 90% of the use cases, but lens-family may suffice. This may warrant another proposal in itself since there are many things to consider.
Definitely, they are really useful
I'm still haven't written the proposal because I'm still thinking a bit about what to write and what not. Today I stumbled upon into RIO, a Prelude that @snoyberg is trying to make the standard of Haskell, looks really great and I'm sure that we can borrow some ideas/build on top of that/use it.
Sure, sounds good. RIO looks interesting but it looks like it has a lot of dependencies. I wonder if it's better to keep the prelude minimal and provide a list of libraries that should be used for standard use cases (i.e. immutable data structures via containers
, etc). Thoughts on this?
One of the things that I like about, say Java, is that you can import HashMap
and use it directly when you need it. No need to add a library to your project, RIO dependencies', which by the way are these (so other readers dont have to go to RIOs repo)
dependencies:
- base >= 4.9 && < 10
- bytestring
- containers
- deepseq
- directory
- exceptions
- filepath
- hashable
- microlens
- mtl
- primitive
- text
- time
- typed-process >= 0.2.1.0
- unliftio >= 0.2.4.0
- unordered-containers
- vector
Seem reasonable to me. I would even probably throw async
in. As all of those are used nearly in 90% of the projects (in my opinion) :smile:
So to be clear, the discussion is about a prelude for Eta, which is a module whose functions/types are imported into all Eta programs without even using an import statement. When comparing with Java, that's equivalent to java.lang.*
which is quite tiny.
What you're suggesting is that we have an eta-base
with all those dependencies - that's a separate discussion altogether and I agree with what you've stated above like including standard data structures and such.
@NickSeagull I've filed #657 to discuss the standard library itself.
So the prelude for RIO is this file, which is what we should be comparing against when discussing about the prelude: https://github.com/commercialhaskell/rio/blob/master/src/RIO.hs
Yep @rahulmutt , I completely messed up :sweat_smile: . Makes much more sense to have an eta-base
:)
I've just written the proposal: https://github.com/typelead/eta-proposals/blob/ccd129716a6267cc27c2ac7538d81c5ea17fce2c/proposals/EP002-NewPrelude.org . Let me know what you think!
I was thinking about the type class functions aliases, wouldn't be weird to tell users that Functor
is a type class that defines map
, but then have them implement the fmap
function in their instances?
I don't see how "for historical reasons" would be a good excuse :thinking:
On the other hand, it would be a great pain to have conversors between the Haskell typeclasses and the Eta ones. Maybe we should leave them as they are, not enforcing special names?
I don't know, here we have to choose between compatibility and good naming.
Another possibilty would be to say
Given that we are compatible with the Haskell ecosystem, the type classes are implemented in this way, but you should use the Eta standard functions.
Of course this is when someone asks why, probably we should not explain why things are like this. Elm has List.map
, Maybe.map
, etc... and nearly no one complains. When someone does, they just explain.
Weird idea, how hard would be to extend the existing type classes in the existing Prelude?
Example:
class Functor f where
fmap :: (a -> b) -> f a -> f b
fmap = map
map :: (a -> b) -> f a -> f b
map = fmap
Do you think this would make sense?
@NickSeagull I think we should leave the standard typeclasses alone. Create aliases? Sure, but mutating the definition might make things very messy.
Rather than thinking about hypotheticals, we can go ahead and make a new prelude and set up the build on the eta-prelude
repo to compile against a good chunk of Hackage so that we can see the impact. To do that, we'll need to implement the -prelude
flag (#308).
Sure, after rethinking it for a while its a mess. But still, in my opinion its better to have a new prelude that enforces our style, and allow the user to do stuff like import Prelude (read)
so Haskell modules are compiled with the regular prelude, and Eta ones, with the new one
More ideas :rocket:
For some functions, we might be interested in having DSL-style functions.
For example this code
data Order = First | Second | Third | Last
instance Enum Order
fromEnum = ...
toEnum = ...
main = traverse_ (enumFromTo First Last) print
could be rewritten as
data Order = First | Second | Third | Last
instance Enum Order
fromEnum = ...
toEnum = ...
main = foreach (from First to Last) print
where this DSL sugar is defined as:
data Keyword = To
to :: Keyword
to = To
from :: (Enum a) => a -> Keyword -> a -> [a]
from x To y = enumFromTo x y
Quite a lot of people in dataHaskell have discussed about the possibility of adoption of NumHask as their prelude.
This is because it has a much better structure for numerical computing. It is described as:
In summary, the library doesn't do anything fancy. But if having to define (*) when you just want a (+) offends your sensibilities, it may bring some sanity.
Some examples:
>>> 1 / 1
1.0
The literal numbers in the divide above defaulted to Float rather than Int.
>>> 1 / (1::Int)
...
... No instance for (MultiplicativeGroup Int)
...
>>> 1 / fromIntegral (1::Int)
1.0
'Float' and 'Double' are 'NumHask.Algebra.Fields.Field' instances.
>>> zero == 0.0
True
'QuotientField'
>>> 1 `div` 2
0
>>> 3 `mod` 2
1
Basically it defines a bunch of different classes based on mathematical concepts that could be really useful for data science/analysis/engineering, having the Java interop, we could easily integrate with Apache Spark, DL4J, etc...
Also, it defines a lot of laws to be tested using QuickCheck
:
Dismiss the comment about NumHask
, I've discussed it with the author and he sees it as completely non-plausible
@Jyothsnasrinivas and I have discussed that we want to see a good ecosystem of data science/ML libraries for Eta, so we'd be happy to see any efforts in the Prelude that can help facilitate what you mentioned above with NumHask
.
@tonyday567 can you comment here on why you think it's non-plausible so that we have your argument for reference?
As much as I love abstract algebra, my only reservations on the new hierachy is that it brings even more abstract mathematical jargon into the picture. If you can outline the benefit of the new hierachy such that it outweighs the cost, I have no issues. We can find a way to explain it nicely with diagrams/cheatsheets to make it easier for beginners.
Nick misunderstood me - It's entirely plausible. At the moment, the library is very integrated with protolude, which I consider to be the top shelf haskell prelude for beginners - mostly because it removes String, and replaces partial functions. The next release of numhask, however, splits the library into numhask and numhask-prelude so that this dependency is removed from the core library.
numhask doesn't do much - pretty much just a bunch of class instances for the algebra we learn from middle school (it could almost be called arithmetic!). My very, very short pitch is that we are all so very familiar with +, -, and / that it makes sense to separate their classes as there are things that can + but not . The arithmetic operators are so ingrained in peoples minds they should be the go to API for things that have laws like association, distribution and such.
Oh, sounds awesome!! Sorry for my misunderstanding :sweat_smile:
@tonyday567 Thanks for the input! @NickSeagull You can go ahead and include discussion about numhask
inspired typeclass hierarchy for the Eta prelude. @tonyday567 If you have any other suggestions in terms of how we go about implementing it, feel free to chime in.
For now, I've finished a preliminary version of the Prelude. Feel free to criticize.
I think it is ready to be included, if stuff is missing we can just keep updating the Prelude :smile:
I've take a look to Protolude and it looks very nice. Concretely i think we can consider include in eta prelude those design points:
Some of the other features (foldable/traversable) would be got if we upgrade base.
@NickSeagull the actual proposal is great overall. However i'd like to make some comments:
base
with those operators and later, if they are widely used, add them to Prelude<+>
operator for mappend
can look a bit strange for some instances:
Arrow
>>> getProduct (Product 3 <+> Product 4 <+> mempty)
12
fst
, snd
,...), to help users coming form haskell or trying to learn using haskell resources. @jneira As for operators, we won't know whether they are effective until we try them out. I feel that if we put them in a separate module in base
, they will never really be used unless we use them throughout all the main docs.
The Arrow
abstraction is rarely used and I think is considered obselete at this point. so I'm not too worried about that. In practice, I have seen (and myself used) the first
and second
methods and sometimes &&&
and ***
, but the others are fairly obscure.
I don't think we need to tune the Prelude for Haskell users - Haskell users already will have their own personal prelude of choice and will continue to use that for their projects. The beginners from other more widely used FP languages (Scala, Clojure, F#, Elixir, etc.) are the audience for the prelude.
@NickSeagull Your thoughts on this?
Well, if eta continues supporting the haskell prelude for .hs files as default, users can use them if they want, so we can have both options avaliable (and optionally use in each one the default prelude of the other). In fact head:: [a] -> Maybe a
will break most of examples and tutorials out there so breaking things can be fine.
Re <+>
, Apart of the clash with the arrow operator, i keep thinking the additional +
can feel a little strange (dont know if there are more cases but Product
....)
Yes, .hs
files will still use the Haskell Prelude by default, otherwise we will have massive breakage of eta-hackage
! The prelude we're discussing now will only be automatic for files with the .eta
extension.
I agree that <+>
can feel a bit strange. I think @NickSeagull was trying to make it look like this. The +
will bias the reader towards addition and as you said, and Product
-like monoids will look weird. I missed this point in your earlier post.<>
, on the other hand is not biased towards a particular mathematical operation.
Yep, my intention was to make it look like the one that you linked @rahulmutt .
Still, I think that our brains have the monoids wired through addition and it will help newcomers to understand the concept easily.
Another source where you can take inspiration is relude
:
You can find the short overview of relude
here:
In some sense relude
closer to protolude
than to RIO
but has a lot of differences from protolude
. The idea is to be minimal but still useful. One key point there is custom HLint rules. I think if you develop custom prelude which is different from the standard Haskell prelude, it's a good idea to include HLint rules to help beginners to use your prelude as well. The nice thing about relude
is that HLint rules are generated via Dhall. I see some discussion around Dhall and Eta in #704. If you can specify .cabal
files with Dhall, the custom HLint can take some fields from the package configuration (like default-extensions
).
Sure @chshersh, relude is great!
By the way, what should be the next steps to push this forward? I'm really willing to help with this so we have a sane prelude
@ChShersh Really awesome suggestions. I like the idea of specifying hlint rules in the cabal file or maybe even in an external file which you can specify in the cabal file. Will need to think over the long term consequences and make think more about the UX. Perhaps you'd like to make a proposal?
@NickSeagull
1) Make a -prelude
flag that you can send into the compiler which takes a module name as an argument. The default value will be Prelude
(the Haskell prelude) and as we move forward we can change the default to your work on eta-prelude
.
2) Make a new field in the cabal file called prelude
which lets the user specify the prelude for a given component of their project which will send the -prelude
flag to the compiler from (1).
Happy to provide assistance as needed. The changes should (hopefully) be minor.
My preferred alternative prelude is https://github.com/qfpl/papa - I think a -prelude flag would be pretty useful for libraries like it.
As explained on #308 , we'd probably have a custom prelude for Eta.
I'd like to suggest that we could put some major changes on it that make things easier for newcomers to Eta/Haskell, but make it optional for people that already come from Haskell.
For instance, I'd really like the following changes for some operators:
$
by<|
|>
that isflip ($)
<<<
and the flipped with>>>
(Comes fromControl.Arrow
)<$>
by<$|
and create|$>
forflip (<$>)
<*>
by<*|
and create|*>
forflip (<*>)
Most of these operators are great for writing in a fluent style, which coming from Java, is very natural:
Also, I think that it'd be great if we had:
read
toread :: String -> Maybe a
It'd be great if we used this opportunity to remove all the non-sense from the Haskell Prelude and made adoption easier coming from other languages :smile: