New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Module-level functions #24
Conversation
So the proposal is to allow stuff like this? package;
// import Main.hello;
class Main {
static public function main():Void {
hello();
}
}
function hello(){
trace('hello function');
} Could main even be just a function if user wanted is that part of your proposal? package;
function main():Void {
hello();
}
function hello(){
trace('hello function');
} So essentially in more general case this: package myStuff;
class MyFunctions{
static function hello(){
trace('my function hello');
}
} could be written: package myStuff;
// package myStuff.MyFunctions;
function hello(){
trace('my function hello');
} Interesting proposal if I have understood but seems kind of like icing for the user especially if it worked like below, your just kind of removing the 'class' word. package myStuff.MyFunctions;
function hello(){
trace('my function hello');
} but then you could not also put a class in the file, and could you have two groups of functions in a file and import them separately surely the extra of a class makes little odds? Is it not acceptable if no constructor to allow users to drop using static accept that I guess via reflection you could still create a class so you would need different holder name. package myStuff;
functions MyFunctions{
function hello(){
trace('my function hello');
}
} Which seems a lot of change just so that users can avoid word 'static' and looks pretty the same as now. I think the proposal could do with samples of how code might look atleast in your prefered solution, it would make it clearer for people who struggle to read technical definitions but are fine seeing code patterns. So would constants or variables be in these packages ? package myStuff;
const helloText = 'hello';
function hello(){
trace( helloText );
}
function hello2(){
trace( helloText + ' ' + helloText );
} |
Yes, I mentioned that. In fact, it'll automatically work if the implicitly created class for functions will be the primary module class.
That's the intention - when you don't do OOP, this class wrapping becomes just a visual clutter. And it's not just the
That's not true. This proposal allow for placing module-level functions/vars along with other types just fine, that's the idea.
it's mentioned in the proposal as well, that's one of the open questions, but i think it wouldn't hurt to support that as well.
thanks, I added a sample. however you seem to have read the proposal carelessly, because most of what you're asking about was mentioned there... maybe it's because of my english though. :) |
Allow marking those as private as well? I think private function rand() return Math.random();
var MY_RANDOM_NUMBER = rand(); Edit: thinking about it metadata and docs would be useful as well. |
Yes, private functions should totally be supported, though it just occured to me that haxe.macro.Expr structures doesn't have a notion for other |
Maybe the easiest way to implement this is to put all top level functions as statics automatically in the module named class and disallow the definition of a class named as the module itself. This way it would only be nice sugar for this common case. |
Yes that's what I proposed. I also propose a more complex option, but I think we should settle with this. |
The example helps thank you. Your english is fine, code examples really are much clearer than words for some of us, even if they don't cover all the detail. I don't really understand what frabbit said maybe a specific example would help. Looking at the example you added, I am still not clear on how you would prefer/imagine package/import will work in practice, the example glosses over thier use, could you add an example of your prefered package and import use. For instance if I had a file MyMath, and you have a group of functions called trig, and a group called matricies in the MyMath file, and you wanted to use one in one class and one in another, what would that actually look like, or is that not supported? I understand the proposal is for functional approaches to be more clear, but it seems it is just sugar. I don't really see it adding much beyond creating 2 ways to do one thing, which will make learning more complex overall - even if initially it's simpler, your adding a lot of new - and it all needs explaining, even something like lots of slightly different ways of doing 'if' add to the amount people need to learn before they can be productive. The proposal could make for cleaner haxe code and make it clearer that Haxe has functional leanings, but it's hard for me to see it as more than a conceptial icing change, I don't see the bigger picture of how this is more than just reducing keystrokes maybe that's enough but not historically! |
Interesting. It is basically syntactic sugar, but so was the arrow notation for functions and people were pretty happy with that. Some more issues should probably be clarified:
Typically e.g.
Should this compile?
Can the module-level functions be declared
Is there any way to access the module-level functions of a module with reflection? Currently the reflection and type APIs can only handle classes and instances, I don't think they have a notion of modules. Is a function declared at module level a function instance? What is its lifetime? |
This would make explaining Haxe and documenting it much easier, most examples don't need classes and could run/tested this way. Making small tools would also be easier since a main function and maybe some other function can already be a full Haxe application. Its not that big deal to wrap a My only concern is that I think you directly want module-level variables too, otherwise that will confuse ("why can I use function here, but not var here?"). Also try.haxe could remove it's "simple" mode, which now wraps everything in a class with static main function. @clemos |
@Aurel300 since the idea is to place module-level functions in an implicitly created class (so they end up being its static methods), they are going to work just fine as macros and static extensions. and reflection is a good point in favor of making that class the primary module class (so you |
@markknol: on try.haxe, it's indeed just a matter of shortening code samples to a minimum, not at all about advocating for a more "open" way to define functions or variables. |
@clemos I can see the appeal functional programming. A lot of times, I would like to do something like
And:
Other functions, like So yes, importing a module with a bunch of functions like this would "pollute" your namespace, although naturally any variables or functions you define locally override the imported ones. However, there are situations when you are writing very FP-focused code and it would be beneficial (for legibility and nice syntax) to actually have them in the namespace. |
Hey guys, can we proceed with this one? I think we have enough 👍 for it to be merged. |
The core team should decide here. |
This is one of the few things preventing me from switching from TypeScript to Haxe, so this gets a big 👍 from me.
In addition to first-class functions, Haxe also has ADTs (e.g. enums) and pattern matching, which is very unusual for an OOP language (but definitely a good thing!) Haxe definitely has the potential to be an excellent multi-paradigm language, supporting both OOP and functional style seamlessly. It just needs a little nudge in the right direction (e.g. this proposal).
If functions are "elevated" into static methods of an implicit class, couldn't top-level I think top-level var counter: Int = 0;
public function increment(): Int {
return ++counter;
} Or to define static data structures which are shared between functions: var foo = {
// various properties defined here
};
public function bar() {
// do stuff with foo
} |
Yes, I mentioned toplevel |
As per your proposal, we will have a way to execute code at the module? We will be able to call functions at the module-level? |
@fullofcaffeine if you're talking about arbitrary module-level expressions, then no, it's only about defining module-level functions and vars |
Fair enough, I guess module-level expressions would just overcomplicate things, since we would have to assume code would be executed upon importing modules, which could lead to unwanted side-effects. I think the proposal is good as it is. |
Everyone with a decent understanding of functional programming will consider nesting functions in a class as a minor nuisance at best. Even Scala requires global functions to be defined on some object. People drawn to functional programming will choose Scala over Haxe for a pretty long list of reasons, none of which is about having to type a keyword more or less. The benefits of this proposal seem negligible to me, as opposed to the complexity added. If you want to do FP practitioners a favor, that's much appreciated, but this just ain't it ;) |
It just doesn't hold a candle to the convenience of e.g. Ruby. Haxe has
very different goals and ideals than most scripting languages, and that's
fine
Hmm... Haxe is certainly more convenient than Javascript in many regards,
and still, Javascript is considered to be a scripting language. Would you
mind to elaborate on this statement?
…On Thu, Aug 17, 2017 at 5:06 PM, Jeff Ward ***@***.***> wrote:
*On topic:* my reaction is "meh" -- I see @back2dos
<https://github.com/back2dos>'s point. A few lines of class / static /
import isn't a barrier to adoption. As a "learning tool", this won't move
the needle (e.g. as oposed to try.haxe.org which is a great learning /
example-sharing tool.)
And for scripting -- I'll just say it: Haxe isn't really convenient enough
that I've ever wanted to write a quick script in it. It just doesn't hold a
candle to the convenience of e.g. Ruby. Haxe has very different goals and
ideals than most scripting languages, and that's fine.
But meh. I don't have any real arguments against the proposal.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#24 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAE9YLsMuBdKG028UhDM0Hx1iZMR4nM-ks5sZLlPgaJpZM4Ob-Op>
.
|
Would allowing Main.hx to have the hxml commands above package; feel more scripty especially if there was a haxelib to generate a range of these single file templates along with the function changes. It's nice to have a single file for scripts and having a compile.hxml and Main.hx separately is not so ideal. I can't see that it would not be possible to allow/implement a mixed Main file? But doubt if anyone would think it is a good idea beyond me :) sorry off topic. I would not be something you would use for more than scripts and tests. |
Maybe not exactly hxml, but starting with the unix "#!" might work - all the parser need to do is ignore it, like: #! haxe -lib A -D B --run
trace("Hello!"); |
@hughsando Linux only allows for a single argument in the shebang, so that won't work. Nix works around that limitation by doing this: #! /usr/bin/env nix-shell
#! nix-shell -i real-interpreter -p packages The second Nix already supports both Haxe and haxelib, so if Haxe ignored all lines starting with #! /usr/bin/env nix-shell
#! nix-shell -i ./haxex.sh -p haxe
class Main {
static function main() {
trace("Hello world!");
}
} But this is starting to get very off-topic, I think that should be in a separate proposal or issue. |
Sure. My types of "scripting" tasks are typically shell scripts. Running external commands and munging (aka processing) text files. So I imagined myself a random scripting task -- count all the "x" characters in all the
Now, there're all kinds of questionable practices there. And it may look like Greek to many, but the process of constructing such a program [in a REPL, with Ruby language features] is insanely fast. I answered my question in less than a minute. That's what a quick scripting language does for me. Haxe -- I can't imagine how long or how many lines of code it'd take me to do this (in fairness, partially because I don't do this type of thing in Haxe.) But I use Haxe to build large, reusable systems. Without compile-time type safety, Ruby and JS are a nightmare at that. They're different tools. I use both. It would take massive syntax / paradigm changes for Haxe to beat the convenience of Ruby at these kinds of scripting tasks. Skipping Ok, for comparison's sake, I coded the above in Haxe. Here's what I ended up with: https://hastebin.com/zeyudaruci.cs Ignoring the time I spent looking up various APIs (which wasn't really Haxe's fault), there are still a bunch of minor inconveniences that all add up:
|
I think most of these problems come from the process api - and maybe that is what makes a good scripting library. But maybe not. I think its just a library thing. Ideally you would do something like this: var sum=0;
FileSystem.findMatching(srcDir, "*.hx", fn -> sum+= File.getContent(fn).split("x").length-1);
println(sum); Which is only one utility function (findMatching) away from being 1:1 code match. On the plus side, the haxe will work on windows without cygwin, and this is why I use haxe for my scripting, and write the utility functions as required. eg https://github.com/haxenme/nme/blob/master/tools/make_classes/MakeClasses.hx#L7 is pretty similar. |
Ok, I took a moment to think about it and I apologize for the length of my post. Then again I hope it contains all I have left to say on the matter ;) First of all, "X is an interesting point but should be put in a different proposal" just doesn't work. Take the optionality of the package statement I brought up. You can consider that a different question, but there's no wisdom in doing so. The basic question is the same - "what is the guiding principle?" - and the possibility to omit the wrapping class or the package statement are part of the same answer. If you make that two questions and answer them differently, you will violate the principle of the least surprise. This is a no-go, especially if your declared goal is to make the language more palatable for newcomers. Good design is driven by principles, not by features. We're headed the opposite way here. So look: if after 12 or so years we wish to reconsider/reformulate/realign Haxe's principles, I very much welcome that. But that's not at all what is happening. Beyond the risk of inconsistency, there is also the risk of self-incapacitation. One can pretend that the idea to make Haxe "syntactically more suitable for scripting" is entirely separate. I don't see that. It is only a separate question once you can guarantee separation, which requires a concrete solution for having this idea and the other at the same time. That may be disambiguation per file extension or what not. But we need to explore this problem at least to the point of understanding how much the cost of adding the second will increase by adding the first and count that towards the price of adding the first. Language features do not exist in a vacuum. Some of my favorites:
It is pure hubris to think that you can simply add things to a design because for the moment it seems cheap and you can't think of future problems down the road just yet. Perhaps a little retrospective by Tony Hoare makes that point better than I ever can:
Yes, of course you cannot predict all future problems. But you should really make an effort instead of simply resorting to "this is not part of the proposal". And even if you cannot think of anything, it is always more prudent to assume that adding complexity has an inherent cost, so if you can't make a strong case for the benefits (and I don't see it here) that passes a compelling threshold, the arguments simply do not weigh in your favor. The burden of proof should be shouldered by those defending the proposal, not those challenging it. FWIW, the more I think about the proposal, the more I dislike the implementation details, because it's built on a pretty leaky abstraction. You have this implicit class and you need to take care that it doesn't pop up everywhere. It should not be in the I think that can be remedied. My advise would be to support it properly instead of resorting to such hackery: have top level vars and functions in all of the type system (not just rtti and documentation and macros which also need to treat As far as reflection is concerned, I would add that there should be something like @Pauan Look. My personal opinion is that Haxe's syntax is quite baroque and could benefit from conciseness. Here is what I do: I get over it. And yes, it's my genuine opinion that people who give such syntactic trivialities enough weight to pick one language vs another have no shortage of either of the concrete mental qualities I ascribed to them. Obsessing over syntax is a guilty pleasure that I sometimes also find hard to resist and while I sympathize I do not even in the slightest consider it reasonable and I don't see that I have to. Nor do I see that anything becomes better by long-winded posts about how writing a little less code is such a huge benefit (see the irony?). I called no-one a moron and I don't insult people who disagree with me. To make such claims is baseless (I don't disagree with your preference), slanderous and intellectually lazy. Please refrain from doing that and we'll be just fine. Thank you. We share the same preference. That doesn't mean I will blindly accept flimsy, speculative or even quite fallacious arguments just because they support my point of view. If all that we're left with is "I like it better this way" then that's what we have and trying to conceal it behind a grand discourse is just dishonest. I'm sure you can do better than that and I invite you to do so ;) I absolutely agree with Hugh that Haxe makes for a great scripting language and after overcoming some initial pain it has become my weapon of choice. The speed of Haxe's static analysis makes its cost negligible for scripting tasks and the runtime speed of eval is just fine (I'm pretty optimistic it beats Ruby for CPU-intensive stuff). Pursuing this is a worthwhile goal and would entail:
With the rise of devops and automation, scripting is no longer just a matter of quickly hacking something together in an interactive shell that you fire and forget, but by growing robust and maintainable processes and I believe Haxe could meet the increasing demand really well. We'd have to want it and then to communicate it. I believe we should, because it is something that will be relevant to existing Haxers (some of whom still use gulp or maven or whatnot) and can truly drive further adoption. The first step - and I cannot stress this enough - should therefore not be made harder by this proposal. With that provided, I don't care much about what happens to this, although I believe that a less hacky approach will pay off in the long run. |
Thanks for the post @back2dos . I was originally mostly supportive of the proposal, but now I think the time "saved" is better spent by simply having e.g. a script which generates boilerplate code (an approach I take for my Haxe scripting), or an IDE which does the same. As for utility functions which would be more "accessible" by being module-level, static extension is fine, or a short, one-letter module name if necessary. As for the entire scripting tangent … While I do agree the standard Haxe API is lacking in some respects and inconsistent in many cases, this is a job for a specific library. It could provide simple functional programming for file glob patterns, directories, etc etc – whatever is generally used in scripting / batch automation. |
Any news here? 🙄 |
There's a bit of stalemate here right now. I still think that module-level functions would be a nice addition to the language without introducing a lot of new concepts to the user. At the same time, after what @back2dos said and what we discussed on Slack, I'm thinking that if we want to have module-level functions/vars, maybe we should indeed introduce them as proper type-system citizens instead of having a wrapper class auto-generated. But that's a lot more intrusive change wrt implementation, so I'm not sure if it's really worth it... |
No point over-complicating the implementation. From the c++ backend point of view, the auto-gen class would work well. Any concerns w.r.t. reflection can be unit tested relatively easily and implemented gradually, which makes things easier. If you can't tell from the outside what implementation is used, then you can't care about it. |
@hughsando I think the concern here is about macro representation mostly. Anyway there are still three options and I'm personally fine with either:
|
Yeah, the macro thing requires some thought. |
I think that autogen class by default is good, and then because theses
classes have a specific cl_kind the platform generator can choose to remove the class
wrapper if it supports top level functions.
…On Wed, Oct 11, 2017 at 8:35 AM, Hugh Sanderson ***@***.***> wrote:
Yeah, the macro thing requires some thought.
There could always be a default implementation that bundles the module
items into a default class immediately prior to the backend, so you don't
need to change the backends, no matter what the internal representation is.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#24 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA-bwKzFXXzD7CxHRUIB9ZsLbWanBWYlks5srGHHgaJpZM4Ob-Op>
.
|
So do we all agree on how to proceed here? I just don't want the PR to be |
Hey this PR is only a couple months old :) Anyway, I'll look into actual implementation. |
@nadako Almost half a year :) Anyway, no pressure whatsoever, we're an open-source community after all and it's all voluntary work. I just pinged to know the status. Btw, shouldn't we merge this? We got enough votes from core-team members no? Cheers! |
Most people like the idea, while the actual implementation regarding macro structures is still kind of in question, yet it's described in the proposal, so I'm unsure about merging it in its current state. |
I missed the proposal but I want it! I'm happy with an implicit module class. Please make it happen! |
Could we allow lowercase filenames? Perhaps lowercase filenames could be functional, and uppercase could be modular (though I'm generally in favor of no special magic based on file cases) BTW, this could be helpful for HXP-style replacements of HXML build.hx
|
Is this on the roadmap for haxe 4? IMO it would be a great addition. |
Will this ever be accepted and merged? 🤔 |
I think so, but probably not for Haxe 4.0 |
Allow defining functions (and maybe vars) directly in .hx files instead of making a class with static methods.
Rendered version