1.  

    Thinking about how to do imports in Pikelet! I recently merged support for universe ‘shifting’ in Pikelet too, but I’m still not happy with my documentation on it. My technical writing still needs some practice before it meets my own expectations…

    1. 1

      I did not find this explicitly mentioned, but surely the name resolution in OOP is dynamic rather than lexical?

      1. 2

        Depends on the language doesn’t it?

        1. 1

          I would argue that message passing, with the objects deciding what to do about a message received at runtime captures the essence of dynamic binding.

          However, I agree with your point. OO is somewhat amorphous at this point, and one may very well implement a language without late binding and call it OO.

          Edit: To clarify what I meant; dynamic binding is where the execution path rather than the lexical ordering determines how a method or variable is resolved. When an object receives a message, the message resolution is determined by the execution path that involved that object. That is, one can not predict in advance using lexical knowledge alone what method will be called.

          1. 8

            The post talks both about name resolution (lexical and dynamic scope) and method resolution, and sometimes seems to conflate the two.

            All mainstream OO languages that I’m familiar with use lexical scope, while most of them use dynamic binding.

            1. 1

              Yes, agreed. Got muddled :/

      1. 8

        Always a joy to read Conor’s writing :)

        Note that Epigram has been dead for a while, Idris is its spiritual successor (I believe it actually evolved from an attempt to build an Epigram compiler). Idris is explicitly aiming to be a “real” programming language; Agda is very similar, but is more often used from a mathematical/logical side of Curry-Howard, rather than the programming side.

        Neither Idris or Agda have the 2D syntax of Epigram, but they both have powerful Emacs modes which can fill in pieces of code (Haskell’s “typed holes” is the same idea, but (as this paper demonstrates) Haskell’s types are less informative).

        1. 10

          Indeed, I suppose it’s that Idris evolved from what was intended to be the back end of Epigram. It certainly owes a lot to Conor McBride and James McKinna’s work on Epigram. I don’t know if “real programming language” is exactly the right way to put it, though, so much as being the language I wanted to have to explore the software development potential of dependent types. Maybe “real” will come one day :).

          1. 1

            Will we see a longer form post/paper or something that isn’y merely Twitter teasers about Blodwen anytime soon? :)

            1. 6

              Hopefully! I know I need to get on with writing things so that more people can join in properly. I have plenty of time to work on it for a change in the next few months, so that’s nice…

              1. 1

                Do you have a writeup about it? I’m wondering why you’re replacing Idris which is somewhat established already, I mean that probably is the reason you’re replacing it, but still I wonder what concretely necessitated a whole new language instead of a 2.0

                1. 5

                  It isn’t a whole new language, it’s a reimplementation in Idris with some changes that experience suggests will be a good idea. So it’s an evolution of Idris 1. I’ll call it Idris 2 at some point, if it’s successful. It’s promising so far - code type checks significantly faster than in Idris 1, and compiled code runs a bit faster too.

                  Also, I’ve tried to keep the core language (which is internally called ‘TTImp’ for ‘type theory with implicits’) and the surface language cleanly separated. This is because I occasionally have ideas for alternative surface languages (e.g. taking effects seriously, or typestate, or maybe even an imperative language using linear types internally) and it’ll be much easier to try this if I don’t have to reimplement a dependent type checker every time. I don’t know if I’ll ever get around to trying this sort of thing, but maybe someone else will…

                  I started this because the Idris implementation has a number of annoying problems (I’ll go into this some other time…) that can only be fixed with some pretty serious reengineering of the core. So I thought, rather than reengineer the core, it would be more fun to see (a) if it was good enough to implement itself, and (b) if dependent types would help in any way.

                  The answer to (a) turned out to be “not really, but at least we can make it good enough” and to (b) very much so, especially when it comes to name manipulation in the core language, which is tricky to get right but much much easier if you have a type system telling you what to do.

                  I don’t have any writeup on any of this yet. It’ll happen eventually. (It has to, really - firstly because nobody ever made anything worthwhile on their own so a writeup is important for getting people involved, and secondly because it’s kind of what my job is :))

              2. 2

                I just follow along with the commits. Edwinb is usually pretty good with his commit messages, so you can kind of get a story of the development from that! :)

                1. 1

                  I’ve got to admit it’s very weird reading a reply by someone with your identical name/spelling, thanks!

                2. 1

                  What’s Blodwen?

                  1. 2

                    An implementation of Idris in Idris: https://github.com/edwinb/Blodwen/

                    Has a neat implementation of Quantitative Type Theory that I’m hoping to integrate in my own programming language!

                    1. 1

                      Nice! What’s your language? Btw your second link is broken

                      1. 3

                        Fixed! This is mine: https://github.com/pikelet-lang/pikelet - scratching my itch of Rust not being enough like Idris, and Idris being not designed with low level systems programming in mind. Probably won’t amount to much (it’s rather ambitious), but it’s been fun playing around, learning how dependent type checkers work! I still need to learn more about what Epigram and Idris do, but it takes passes of deepening to really get a handle on all the stuff they learned. I’m probably making a bunch of mistakes that I don’t know about yet!

                        1. 1

                          Nice logo. Looks harmless and fun.

                        2. 2
                3. 1

                  Thanks for putting this in context, that’s really useful.

                  Also: sounds like I’m missing a (200x) in the title, if you know the correct year.

                1. 27

                  I’d like to provide a more sympathetic outside perspective.

                  There are a few common complaints about Elm and Elm community:

                  • Blocking discussion (locking threads etc.)
                  • Insufficient communication about Elm development
                  • Things getting removed from the language.

                  With regards to blocking discussion, I think the logic is something like this:

                  • The core developers have a roadmap for Elm development and they want to stick to it
                  • They tried including more developers but haven’t found an effective way to deal with more contributors
                  • Therefore, they have limited time
                  • They can spend this time rehashing the same arguments and debating half-baked ideas, or they can spend this time following their roadmap, but not both.

                  I would prefer that the discussions weren’t removed or locked, but on the other hand, it’s got to be grating to deal with the same entitled, uninformed or complaining comments all the time. I’ve read most of these discussions, and other than people venting, nothing is ever achieved in them. My reflexive reaction is to be uncomfortable (like a lot of other people) but then, there is also a certain clarity when people just say that they will not engage in a discussion.

                  With regards to insufficient communication, I think the main things to understand is that Elm is an experiment in doing things differently, and it’s causing a clash with conventional understanding. Elm is about getting off the upgrade treadmill. So, for example, when a new release like Elm 0.19 comes out, it happens without a public alpha and beta phases, and it’s not actually the point where you go and immediately migrate your production code to it! It’s only the point to start experimenting with it, it’s the point where library and tool authors can upgrade and so on. (There was quite a bit of activity prior to release anyway, it just wasn’t advertised publicly.)

                  Finally, the most contentious example of a “feature” getting removed is the so called native modules (which basically means the ability to have impure functions written in JS in your Elm code base). As far as I can tell (having followed Elm since 0.16), native modules were always an internal implementation detail and their use was never encouraged. Nevertheless, some people started using them as a shortcut anyway. However, they were a barrier to enabling function-level dead code elimination which is the main feature of the 0.19 release, so the loophole was finally closed. Sure, it’s inconvenient for people who used them, but does anyone complain when, say, Apple removes an internal API?

                  Ultimately, Elm is just an open source project and the core maintainers don’t really owe anybody anything - no contracts are entered into and no funds are exchanged. They can do whatever they want.

                  Of course, there is a question of the long term effects this approach is going to have on the community. Will it alienate too many people and cause Elm to wither? Will Elm remain a niche language for a narrow class of applications? That remains to be seen.

                  1. 23

                    but on the other hand, it’s got to be grating to deal with the same entitled, uninformed or complaining comments all the time.

                    Over the years, I have come to believe this is a vital part of building a community. Using draconian tactics to stomp out annoying comments is using power unwisely and worse yet – cripples your community in multiple ways.

                    The first thing to remember is that when a comment (entitled, uninformed or otherwise) comes up repeatedly – that is a failure of the community to provide a resource to answer/counter/assist with that comment. That resource can be a meme, a link, an image, a FAQ, a full on detailed spec document, whatever. This type of thing is part of how a community gets a personality. I think a lot of the reason there are a bunch of dead discourse servers for projects is too stringent policing. You should have a place for people to goof off and you have to let the community self police and become a real community. Not entirely, obviously, but on relevant topics.

                    This constant repetition of questions/comments is healthy, normal, it is the entrance of new people to the community. More importantly, if gives people who are just slightly deeper in the community someone to help, someone to police, someone to create resources for, even to a degree someone to mock (reminding them they aren’t THAT green anymore) – a way to be useful! This is a way to encourage growth, each “generation” of people helps the one that comes after them – and it is VITAL for building up a healthy community. In a healthy community the elders will only wade in occasionally and sporadically to set the tone and will focus on the more high minded, reusable solutions that move the project forward. Leave the minor stuff be done by the minor players, let them shine!

                    Beyond being vital to build the community – it is a signal of where newcomers are hurting. Now if documentation fixes the problem, or a meme… terrific! But if it doesn’t, and if it persists … that is a pain point to look at – that is a metric – that is worth knowing.

                    1. 5

                      Yeah, each one of these people gives you a chance to improve how well you communicate, and to strengthen your message. But shutting down those voices then run the risk of surrounding yourself with ‘yes people’ who don’t challenge your preconceptions. Now, it’s entirely up to the Elm people to do this, but I think they are going to find it harder to be mainstream with this style of community.

                      Note that I’m perfectly fine with blocking and sidelining people who violate a CoC, or are posting hurtful, nonconstructive comments. You do have to tread a fine line in your moderation though. Being overly zealous in ‘controlling the message’ can backfire in unpredictable ways.

                      Anyway, I continue to follow Elm because I think the designer has some excellent ideas and approaches, even if I do disagree with some of the ways the way the community is managed.

                      1. 5

                        even if I do disagree with some of the ways the way the community is managed.

                        I don’t think the two jobs (managing the community and managing the project) should necessarily be done by the same person. I actually think it probably shouldn’t. Each job is phenomenally challenging on its own – trying to do both is too much.

                        1. 2

                          Yeah, completely agree! I think it would take a huge weight of that person’s shoulders too! :)

                          1. 1

                            I don’t think Evan personally moderates the forums. Other people do it these days.

                            1. 3

                              But, they do it on his behalf? This policy of locking and shutting down discussions comes from somewhere. That person directly or indirectly is the person who “manages” the community, the person who sets the policies/tone around such things.

                              I personally have no idea, I am not active in the Elm community.

                              1. 1

                                I’m not sure who sets the policy and how.

                        2. 2

                          That’s a very interesting perspective, thanks.

                        3. 12

                          I’ll add the perspective of someone who loved Elm and will never touch it again. We’re rewriting in PureScript right now :) I’m happy I learned Elm, it was a nice way of doing things while it lasted.

                          In Elm you may eventually hit a case where you can’t easily wrap your functionality in ports, the alternative to native modules. We did, many times. The response on the forum and other places is often to shut down your message, to give you a partial version of that functionality that isn’t quite what you need, to tell you to wait until that functionality is ready in Elm (a schedule that might be years!), or until recently to point you at native modules. This isn’t very nice. It’s actually very curious how nice the Elm community is unless you’re talking about this feature, in which case it feels pretty hostile. But that’s how open source rolls.

                          Look at the response to message linked in the story: “We recently used a custom element to replace a native modules dealing with inputs in production at NoRedInk. I can’t link to it because it’s in a proprietary code base but I’ll be writing an speaking about it over the next couple months.”

                          This is great! But I can’t wait months in the hope that someone will talk about a solution to a problem I have today. Never mind releasing one.

                          Many people did not see native modules as a shortcut or a secret internal API. They were an escape valve. You would hit something that was impossible without large efforts that would make you give up on Elm as not being viable. Then you would overcome the issues using native modules which many people in the community made clear was the only alternative. Now, after you invest effort you’re told that there’s actually no way to work around any of these issues without “doing them the right way” which turns out to be so complicated that companies keep them proprietary. :(

                          I feel like many people are negative about this change because it was part of how Elm was sold to people. “We’re not there yet, but here, if we’re falling short in any way you can rely on this thing. So keep using Elm.”

                          That being said, it feels like people are treating this like an apocalypse, probably because they got emotionally invested in something they like and they feel like it’s being changed in a way that excludes them.

                          You’re right though. Maybe in the long term this will help the language. Maybe it will not. Some people will enjoy the change because it does lead to a cleaner ecosystem and it will push people to develop libraries to round out missing functionality. In the short term, I have to get things done. The two perspectives often aren’t compatible.

                          I’m personally more worried about what will happen with the next major change where Elm decides to jettison part of its community. I don’t want to be around for that.

                          1. 2

                            If people encouraged you to use native modules, then that was unfortunate.

                            I’m not sure I understand the issue with custom elements. Sure, they’re a bit complicated and half baked but it certainly doesn’t require a research lab to use them (in fact, I’ve just implemented one now).

                            I would agree, however, that the Elm developers have a bit of a hardline approach to backward compatibility. Perhaps there is a misunderstanding around the state of Elm - ie whether it’s still an experiment that can break compatibility or a stable system that shouldn’t.

                            I’m not sure how I feel about backward compatibility. As a user, it’s very convenient. As a developer, it’s so easy to drown in the resulting complexity.

                          2. 10

                            I would prefer that the discussions weren’t removed or locked, but on the other hand, it’s got to be grating to deal with the same entitled, uninformed or complaining comments all the time. I’ve read most of these discussions, and other than people venting, nothing is ever achieved in them. My reflexive reaction is to be uncomfortable (like a lot of other people) but then, there is also a certain clarity when people just say that they will not engage in a discussion.

                            I’ll go one further and say I’m quite glad those discussions get locked. Once the core team has made a decision, there’s no point in having angry developers fill the communication channels the community uses with unproductive venting. I like the decisions the core team is making, and if those threads didn’t get locked, I’d feel semi-obligated to respond and say that I’m in favor of the decision, or I’d feel guilty not supporting the core devs because I have other obligations. I’m glad I don’t have to wade through that stuff. FWIW, it seems like the community is really good at saying “We’re not going to re-hash this decision a million times, but if you create a thread about a specific problem you’re trying to solve, we’ll help you find an approach that works” and they follow through on that.

                            I don’t have a lot of sympathy for folks who are unhappy with the removal of the ability to compile packages that include direct JS bindings to the Elm runtime. For as long as I’ve been using Elm the messaging around that has consistently been that it’s not supported, it’s just an accidental side effect of how things are built, and you shouldn’t do it or you’re going to have a bad time. Now it’s broken and they’re having a bad time. This should not be a surprise. I also think it’s good decision to actively prohibit it. If people started using that approach widely, it would cause a lot of headaches for both the community and hamstring the core team’s ability to evolve the language.

                            1. 6

                              I’m quite glad those discussions get locked

                              and

                              I like the decisions the core team is making

                              Do you believe your perspective would change if you didn’t agree with the developers decisions? Obviously I have a different perspective but I am curious if think you would still have this perspective if you were on the other side?

                              Additionally, just because the core team has “made a decision” doesn’t mean it wasn’t a mistake, nor that it is permanent. Software projects make mistakes all the time, and sometimes the only way to really realize the mistake is the hear the howls of your users.

                              1. 3

                                I’m pretty confident I wouldn’t change my position on this if I wasn’t in agreement with the core team’s choices. I might switch to PureScript or ReasonML, if I think the trade-offs are worth it, but I can’t see myself continuing to complain/vent after the decision has been made. I think appropriate user input is “I have this specific case, here’s what the code look like, here’s the specific challenge with any suggested alternative” If the core team decides to go another way after seeing their use cases, it’s clear we don’t have the same perspective on the trade-offs for those decisions. I can live with that. I don’t expect everybody to share my opinion on every single technical decision.

                                As an example, I use Clojure extensively at work, and I very much disagree with Rich Hickey’s opinions about type systems, but it’s pretty clear he’s thought through his position and random folks on the internet screaming differently isn’t going to change it, it’ll just make his job more difficult. I can’t imagine ever wanting to do that to someone.

                                sometimes the only way to really realize the mistake is the hear the howls of your users

                                It’s been my experience that the folks who can provide helpful feedback about mistaken technical decisions rarely howl. They can usually speak pretty clearly about how decisions impact their work and are able to move on when it’s clear there’s a fundamental difference in goals.

                                1. 2

                                  It’s been my experience that the folks who can provide helpful feedback about mistaken technical decisions rarely howl.

                                  We fundamentally disagree on this point (and the value of the noisy new users), and I don’t think either of us is going to convince the other. So, I think this is a classic case of agree to disagree.

                            2. 10

                              I think what bothers me the most about the core team’s approach to features is not that they keep removing them, but that for some they do not provide a valid alternative.

                              They’ll take away the current implementation of native modules, but coming up with a replacement is too hard, so even though the core libraries can use native code, us peasants will have to do without.

                              They won’t add a mechanism for higher rank polymorphism because coming up with a good way to do it is hard, so even though the base library has a few magic typeclasses for its convenience, us peasants will have to make do with mountains of almost duplicated code and maybe some code generation tool.

                              So where does that leave Elm right now? Should it be considered a production-ready tool just by virtue of not having very frequent releases? Or should it be regarded as an incomplete toy language, because of all the breaking changes between releases, all the things that haven’t been figured out yet, and how the response to requests for ways to do things that are necessary in real code is either “you don’t need that”, which I can live with most of the time, or “deal with it for the moment”, which is unacceptable.

                              I think Elm should make it more clear that it’s ostensibly an unfinished project.

                              1. 3

                                They’ll take away the current implementation of native modules, but coming up with a replacement is too hard

                                They won’t add a mechanism for higher rank polymorphism because coming up with a good way to do it is hard

                                I don’t think this is a fair characterization of the core team’s reasons for not supporting those features. I’ve read/watched/listened to a lot of the posts/videos/podcasts where Evan and other folks discuss these issues, and I don’t think I’ve ever heard anyone say “We can’t do it because it’s too difficult.” There’s almost always a pretty clear position about the trade-offs and motivations behind those decisions. You might not agree with those motivations, or weigh the trade-offs the same way, but it’s disingenuous to characterize them as “it’s too hard”

                                1. 4

                                  I exaggerate in my comment, but what I understood from the discussions around rank n polymorphism I’ve followed is basically that Evan doesn’t think any of the existing solutions fit Elm.

                                  I understand that language design, especially involving more complex features like this, is a hard issue, and I’m sure Evan and the core team have thought long and hard about this and have good reasons for not having a good solution yet, but the problem remains that hard things are hard and in the meantime the compiler can take an escape hatch and the users cannot.

                                2. 2

                                  Should it be considered a production-ready tool just by virtue of not having very frequent releases? Or should it be regarded as an incomplete toy language

                                  I always struggle with this line of questioning because “incomplete and broken” describes pretty much all of the web platform in the sense that whenever you do non-trivial things, you’re going to run into framework limitations, bugs, browser incompatibilities and so on.

                                  All you can do is evaluate particular technologies in the context of your specific projects. For certain classes of problems, Elm works well and is indeed better than other options. For others, you’ll have to implement workarounds with various degrees of effort. But again, I can say the same thing for any language and framework.

                                  Is it good that it’s so easy to bump up against bugs and limitations? No. But at least Elm is no worse than anything else.

                                  Taking a tangent, the main problem is that Elm is being built on top of the horrifically complex and broken foundation that is the web platform. It’s mostly amazing to me that anything works at all.

                                  1. 10

                                    Is it good that it’s so easy to bump up against bugs and limitations? No. But at least Elm is no worse than anything else.

                                    Having worked with ClojureScript on the front-end for the past 3 years, I strongly disagree with this statement. My team has built a number of large applications using Reagent and whenever new versions of ClojureScript or Reagent come out all we’ve had to do was bump up the versions. We haven’t had to rewrite any code to accommodate the language or Reagent updates. My experience is that it’s perfectly possible to build robust and stable tools on top of the web platform despite its shortcomings.

                                    1. 4

                                      I have the opposite experience. Team at day job has some large CLJS projects (also 2-3 years old) on Reagent and Re-Frame. We’re stuck on older versions because we can’t update without breaking things, and by nature of the language it’s hard to change things with much confidence that we aren’t also inadvertently breaking things.

                                      These projects are also far more needlessly complex than their Elm equivalents, and also take far longer to compile so development is a real chore.

                                      1. 6

                                        Could you explain what specifically breaks things in your project, or what makes it more complex than the Elm equivalent. Reagent API had no regressions in it that I’m aware of, and re-frame had a single breaking change where the original reg-sub was renamed to reg-sub-raw in v 0.7 as I recall. I’m also baffled by your point regarding compiling. The way you develop ClojureScript is by having Figwheel or shadow-cljs running in the background and hotloading code as you change it. The changes are reflected instantly as you make them. Pretty much the only time you need to recompile the whole project is when you change dependencies. The projects we have at work are around 50K lines of ClojureScript on average, and we’ve not experienced the problems you’re describing.

                                      2. 2

                                        I think the ease of upgrades is a different discussion. There is a tool called elm-upgrade which provides automated code modifications where possible. That’s pretty nice, I haven’t seen a lot of languages with similar assistance.

                                        My point was, you cannot escape the problems of the web platform when building web applications. Does ClojureScript fully insulate you from the web platform while providing all of its functionality? Do you never run into cross-browser issues? Do you never have to interoperate with JavaScript libraries? Genuinely asking - I don’t know anything about ClojureScript.

                                        1. 3

                                          My experience is that vast majority of issues I had with the web platform went away when my team started using ClojureScript. We run into cross-browser issues now and then, but it’s not all that common since React and Google Closure do a good job handling cross-browser compatibility. Typically, most of the issues that we run into are CSS related.

                                          We interoperate with Js libraries where it makes sense, however the interop is generally kept at the edges and wrapped into libraries providing idiomatic data driven APIs. For example, we have a widgets library that provides all kinds of controls like data pickers, charts, etc. The API for the library looks similar to this to our internal widgets API.

                                          1. 1

                                            Sounds like a great development experience!

                                            Let me clarify my thinking a bit. For a certain class of problems, Elm is like that as well. But it certainly has limitations - not a huge number of libraries etc.

                                            However, I think that pretty much everything web related is like that - limitations are everywhere, and they’re much tighter than I’d like. For example, every time I needed to add a date picker, it was complicated, no matter the language/framework. But perhaps your widgets library has finally solved it - that would be cool!

                                            So I researched Elm and got a feel for it’s limitations, and then I could apply it (or not) appropriately.

                                            I would agree, however, that the Elm developers have a bit of a hardline approach to backward compatibility. Perhaps there is a misunderstanding around the state of Elm - ie whether it’s still an experiment that can break compatibility or a stable system that shouldn’t.

                                            I’m not sure how I feel about backward compatibility. As a user, it’s very convenient. As a developer, it’s so easy to drown in the resulting complexity.

                                            1. 3

                                              Yeah, I agree that the main question is around the state of Elm. If the message is that Elm isn’t finished, and don’t invest into it unless you’re prepared to invest time into keeping up, that’s perfectly fine. However, if people are being sold on a production ready language that just works there appears to be a bit of a disconnect there.

                                              It’s obviously important to get things right up front, and if something turns out not to work well it’s better to change it before people get attached to it. On the other hand, if you’re a user of a platform then stability is really important. You’re trying to deliver a solution to your customers, and any breaking changes can become a serious cost to your business.

                                              I also think it is important to be pragmatic when it comes to API design. The language should guide you to do things the intended way, but it also needs to accommodate you when you have to do something different. Interop is incredibly important for a young language that’s leveraging a large existing ecosystem, and removing the ability for people to use native modules in their own projects without an alternative is a bit bewildering to me.

                                      3. 7

                                        To me the problem is that Elm is not conceptually complete. I listed those issues specifically because they’re both things that the compiler and the core libraries can do internally, but the users of the language cannot.

                                        But at least Elm is no worse than anything else.

                                        No, Elm is a language, and not being able to do things in a language with so few metaprogramming capabilities is a pretty big deal compared to a missing feature in a library or a framework, which can easily be added in your own code or worked around.

                                        1. 1

                                          But how is this different from any other ecosystem? The compiler always has more freedom internally. There are always internal functions that platform APIs can use but your library cannot. Following your logic, we should condemn the Apple core APIs and Windows APIs too.

                                          1. 3

                                            No, what I meant is that the core libraries use their “blessed” status to solve those problems only for themselves, thus recognizing that those problems effectively exist, but the users aren’t given any way to deal with them.

                                            1. 2

                                              But there are actually solutions on offer: ports and custom elements. What’s wrong with using them?

                                              1. 4

                                                Ports are very limiting and require much more work to set up than a normal library, and I haven’t used custom elements so I can’t speak for those.

                                                There’s also no workaround for the lack of ad-hoc polymorphism. One of the complaints I hear the most about Elm is that writing json encoders and decoders is tedious and that they quickly become monstrously big and hard to maintain; often the json deserialization modules end up being the biggest modules in an Elm project.

                                                This is clearly a feature the language needs (and already uses with some compiler magic, in the form of comparable, appendable, and so on).

                                    2. 2

                                      I was hoping to read other points of view on that matter, thanks for taking the time writing down yours!

                                    1. 5

                                      I think it’s worth linking to the whole course too.

                                      It’s a shame the slide show doesn’t respond to mouse clicks below the main text. The arrow keys work, but I spent a minute or so clicking like an idiot before the next slide showed up.

                                      1. 3

                                        Yeah, the lecture notes on the GHC implementation seem a bit easier to digest than the slides.

                                      1. 2

                                        I’ve found bidirectional type checking is indeed a very handy technique for making expressive type systems quickly. I haven’t yet mastered how to marry it with constraint based inference (for implicit arguments) but it proved to be very useful when starting out on building Pikelet.

                                        David Christiansen has a somewhat more acessible intro here. It’s also used in the LambdaPi paper which Pikelet was originally based off.

                                        Let Arguments Go First is another interesting paper that adds an ‘application mode’ that allows you to pull more type information from argument applications. I’m hoping to try to implement that too at some stage!

                                          1. 22

                                            After writing Go for 5 years, I’d recommend Rust for C developers. It’s more complicated than Go for sure, but also has more to offer. The lack of garbage collection and support of generics are definitely a plus compared to Go.

                                            Go is a better language for junior devs, but I wouldn’t call C programmers junior. They should be able to digest Rust’s complexity.

                                            1. 9

                                              They should be able to digest Rust’s complexity.

                                              Non trivial amount of C programmers are still doing C to avoid additional complexity. Not everyone wants a kitchen & sink programming language.

                                              1. 6

                                                Rust can definitely get overly complex if the developers show no constraint (i.e. type golf), but the control afforded by manual memory management makes up for it, IMHO. Unless it’s a one-run project, performance will eventually matter, and fixing bad allocation practices after the fact is a lot harder than doing it right from the beginning.

                                                1. 1

                                                  Couldn’t they just start with a C-like subset of Rust adding from there to their arsenal what extra features they like? It’s what I was going to recommend to those trying it for safety-critical use since they likely know C.

                                                  1. 9

                                                    I think it’s rather difficult to write rust in a C like manner. This contrasts with go, where you can basically write C code and move the type declarations around and end up with somewhat unidiomatic but working go.

                                                    1. 3

                                                      I think C++ as a better C works because you still have libc besides the STL, etc. The Rust standard library uses generics, traits, etc. quite heavily and type parameters and lifetime parameters tend to percolate to downstream users.

                                                      Though I think a lot of value in Rust is in concepts that may initially add some complexity, such the borrow checker rules.

                                                      1. 3

                                                        The problem with C++ is its complexity at the language level. I have little hope of teams of people porting various tools for static analysis, verification, and refactoring to it that C and Java already have. Certifying compilers either. C itself is a rough language but smaller. The massive bandwagon behind it caused lots of tooling to be built, esp FOSS. So, I now push for low-level stuff either safer C or something that ties into C’s ecosystem.

                                                      2. 4

                                                        You could argue the same for C++ (start with C and add extra features). Complexity comes with the whole ecosystem from platform support (OS, arch), compiler complexity (and hence subtle difference in feature implementations) to the language itself (C++ templates, rust macros). It’s challenging to limit oneself to a very specific subset on a single person project, it’s exponentially harder for larger teams to agree on a subset and adhere to it. I guess I just want a safer C not a new C++ replacement which seems to be the target for newer languages (like D & Rust).

                                                        1. 4

                                                          It’s challenging to limit oneself to a very specific subset on a single person project, it’s exponentially harder for larger teams to agree on a subset and adhere to it.

                                                          I see your overall point. It could be tricky. It would probably stay niche. I will note that, in the C and Java worlds, there’s tools that check source code for compliance with coding standards. That could work for a Rust subset as well.

                                                          “I guess I just want a safer C not a new C++ replacement which seems to be the target for newer languages (like D & Rust).”

                                                          I can’t remember if I asked you what you thought about Cyclone. So, I’m curious about that plus what you or other C programmers would change about such a proposal.

                                                          I was thinking something like it with Rust’s affine types and/or reference counting when borrow-checking sucks too much with performance acceptable. Also, unsafe stuff if necessary with the module prefixed with that like Wirth would do. Some kind of module system or linking types to avoid linker errors, too. Seemless use of existing C libraries. Then, an interpreter or REPL for the productivity boost. Extracts to C to use its optimizing and certifying compilers. I’m unsure of what I’d default with on error handling and concurrency. First round at error handling might be error codes since I saw a design for statically checking their correct usage.

                                                          1. 3

                                                            I can’t remember if I asked you what you thought about Cyclone. So, I’m curious about that plus what you or other C programmers would change about such a proposal.

                                                            I looked at it in the past and it felt like a language built on top of C similar to what a checker tool with annotations would do. It felt geared too much towards research versus use and the site itself states:

                                                            Cyclone is no longer supported; the core research project has finished and the developers have moved on to other things. (Several of Cyclone’s ideas have made their way into Rust.) Cyclone’s code can be made to work with some effort, but it will not build out of the box on modern (64 bit) platforms).

                                                            However if I had to change Cyclone I would at least drop exceptions from it.

                                                            I am keeping an eye on zig and that’s closest to how I imagine a potentially successful C replacement - assuming it takes up enough community drive and gets some people developing interesting software with it.

                                                            That’s something Go had nailed down really well. The whole standard library (especially their crypto and http libs) being implemented from scratch in Go instead of being bindings were a strong value signal.

                                                            1. 2

                                                              re dropping exceptions. Dropping exceptions makes sense. Is there another way of error handling that’s safer or better than C’s that you think might be adoptable in a new, C-like language?

                                                              re Zig. It’s an interesting language. I’m watching it at a distance for ideas.

                                                              re standard library of X in X. Yeah, I agree. I’ve been noticing that pattern with Myrddin, too. They’ve been doing a lot within the language despite how new it is.

                                                              1. 4

                                                                Dropping exceptions makes sense. Is there another way of error handling that’s safer or better than C’s that you think might be adoptable in a new, C-like language?

                                                                Yes, I think Zig actually does that pretty well: https://andrewkelley.me/post/intro-to-zig.html#error-type

                                                                edit: snippet from the zig homepage:

                                                                A fresh take on error handling that resembles what well-written C error handling looks like, minus the boilerplate and verbosity.

                                                                1. 2

                                                                  Thanks for the link and tips!

                                                    2. 7

                                                      Short build/edit/run cycles are appreciated by junior and senior developers alike. Go currently has superior compilation times.

                                                      1. 10

                                                        Junior and senior developers also enjoy language features such as map, reduce, filter, and generics. Not to mention deterministic memory allocation, soft realtime, forced error checking, zero-cost abstractions, and (of course) memory safety.

                                                        1. 3

                                                          Junior and senior developers also enjoy language features such as map, reduce, filter, and generics.

                                                          Those are great!

                                                          deterministic memory allocation, soft realtime, forced error checking, zero-cost abstractions, and (of course) memory safety.

                                                          Where are you finding juniors who care about this stuff? (no, really - I would like to know what kind of education got them there).

                                                          1. 8

                                                            I cared about those things, as a junior. I am not sure why juniors wouldn’t care, although I suppose it depends on what kind of software they’re interested in writing. It’s hard to get away with not caring, for a lot of things. Regarding education, I am self-taught, FWIW.

                                                          2. 1

                                                            Map, reduce and filter are easily implemented in Go. Managing memory manually, while keeping the GC running, is fully possible. Turning off the GC is also possible. Soft realtime is achievable, depending on your definition of soft realtime.

                                                            1. 1

                                                              Map, reduce and filter are easily implemented in Go

                                                              How? Type safe versions of these, that is, without interface{} and hacky codegen solutions?

                                                              1. 1

                                                                Here are typesafe examples for Map, Filter etc: https://gobyexample.com/collection-functions

                                                                Implementing one Map function per type is often good enough. There is some duplication of code, but the required functionality is present. There are many theoretical needs that don’t always show up in practice.

                                                                Also, using go generate (which comes with the compiler), generic versions are achievable too. For example like this: https://github.com/kulshekhar/fungen

                                                                1. 9

                                                                  When people say “type safe map/filter/reduce/fold” or “map, reduce, filter, and generics” they are generally referring to the ability to define those functions in a way that is polymorphic, type safe, transparently handled by the compiler and doesn’t sacrifice runtime overhead compared to their monomorphic analogs.

                                                                  Whether you believe such facilities are useful or not is a completely different and orthogonal question. But no, they are certainly not achievable in Go and this is not a controversial claim. It is by design.

                                                                  1. 1

                                                                    Yes, I agree, Go does not have the combination of type safety and generics, unless you consider code generation.

                                                                    The implementation of generics in C++ also works by generating the code per required type.

                                                                    1. 5

                                                                      The implementation of generics in C++ also works by generating the code per required type.

                                                                      But they are not really comparable. In C++, when a library defines a generic type or function, it will work with any conforming data type. Since the Go compiler does not know about generics, with go generate one can only generate ‘monomorphized’ types for a set of predefined data types that are defined an upstream package. If you want different monomorphized types, you have to import the generic definitions and run go generate for your specific types.

                                                                      unless you consider code generation

                                                                      By that definition, any language is a generic language, there’s always Bourne shell/make/sed for code generation ;).

                                                                      1. 1

                                                                        That is true, and I agree that go does not have support for proper generics and that this can be a problem when creating libraries.

                                                                      2. 3

                                                                        That’s why I said “transparently handled by the compiler.” ;-)

                                                                        1. 0

                                                                          I see your point, but “go generate” is provided by the go compiler, by default. I guess it doesn’t qualify as transparent since you have to type “go generate” or place that command in a build file of some sort?

                                                                          1. 1

                                                                            Yes. And for the reasons mentioned by @iswrong.

                                                                            My larger point here really isn’t a technicality. My point is that communication is hard and not everyone spells out every point is precise detail, but it’s usually possible to infer the meaning based on context.

                                                                            1. -1

                                                                              I think the even larger point is that for a wide range of applications, “proper” and “transparent” generics might not even be needed in the first place. It would help, yes, but the Go community currently thrives without it, with no lack of results to show for.

                                                                              1. 1

                                                                                I mean, I’ve written Go code nearly daily since before it was 1.0. I don’t need to argue with you about whether generics are “needed,” which is a pretty slimy way to phrase this.

                                                                                Seems to me like you’re trying to pick a fight. I already said upthread that the description of generics is different from the desire for them.

                                                                                1. -2

                                                                                  You were the first to change the subject to you and me instead of sticking to the topic at hand. Downvoting as troll.

                                                            2. 1

                                                              By superior, I guess you meant shorter?

                                                              1. 2

                                                                Compiling a very large go project with a cold cache might take a minute (sub-second once the cache is warm).

                                                                Compiling a fairly small rust app with a warm cache has taken me over a minute (I think it’s a little better than that now).

                                                                1. 1

                                                                  Yes, and superior to Rust in that regard. Also the strict requirement to not have unused dependencies contributes to counteract dependency rot, for larger projects.

                                                            1. 3

                                                              Wow - great description of Yoneda in the context of Haskell at the end!

                                                              1. 4

                                                                This is a good start with many missing pieces. Haskell section should discuss Stack, for example.

                                                                1. 1

                                                                  Also Bundler for Ruby, the lessons from which heavily influenced the design of Cargo (it was made by the same creators). Could also add NPM?

                                                                1. 6

                                                                  Yep, this is how I figured out monads too, but when using Rust! There is more to them though - the laws are important, but it’s sometimes easier to learn them by examples first!

                                                                  1. 3

                                                                    Can you show an example where a monad is useful in a Rust program?

                                                                    (I’m not a functional programmer, and have never knowingly used a monad)

                                                                    1. 10

                                                                      I learned about monads via Maybe in Haskell; the equivalent in Rust is called Option.

                                                                      Option<T> is a type that can hold something or nothing:

                                                                      enum Option<T> {
                                                                          None,
                                                                          Some(T),
                                                                      }
                                                                      

                                                                      Rust doesn’t have null; you use option instead.

                                                                      Options are a particular instance of the more general Monad concept. Monads have two important operations; Haskell calls them “return” and “bind”. Rust isn’t able to express Monads as a general abstraction, and so doesn’t have a particular name. For Option<T>, return is the Some constructor, that is,

                                                                      let x = Option::Some("hello");
                                                                      

                                                                      return takes some type T, in this case, a string slice, and creates an Option<T>. So here, x has the type Option<&str>.

                                                                      bind takes two arguments: something of the monad type, and a function. This function takes something of a type, and returns an instance of the monad type. That’s… not well worded. Let’s look at the code. For Option<T>, bind is called and_then. Here’s how you use it:

                                                                      let x = Option::Some("Hello");
                                                                      let y = x.and_then(|arg| Some(format!("{}!!!", arg)));
                                                                      
                                                                      println!("{:?}", y);
                                                                      

                                                                      this will print Some("Hello!!!"). The trick is this: the function it takes as an argument only gets called if the Option is Some; if it’s None, nothing happens. This lets you compose things together, and reduces boilerplate when doing so. Let’s look at how and_then is defined:

                                                                      fn and_then<U, F>(self, f: F) -> Option<U> 
                                                                      where F: FnOnce(T) -> Option<U>
                                                                      {
                                                                          match self {
                                                                              Some(x) => f(x),
                                                                              None => None,
                                                                          }
                                                                      }
                                                                      

                                                                      So, and_then takes an instance of Option and a function, f. It then matches on the instance, and if it’s Some, calls f passing in the information inside the option. If it’s None, then it’s just propagated.

                                                                      How is this actually useful? Well, these little patterns form building blocks you can use to easily compose code. With just one and_then call, it’s not that much shorter than the match, but with multiple, it’s much more clear what’s going on. But beyond that, other types are also monads, and therefore have bind and return! Rust’s Result<T, E> type, similar to Haskell’s Either, also has and_then and Ok. So once you learn the and_then pattern, you can apply it across a wide array of types.

                                                                      Make sense?

                                                                      1. 3

                                                                        Make sense?

                                                                        It absolutely does! I’ve used and_then extensively in my own Rust code, but never known that I was using a monad. Thanks for the explanation Steve.

                                                                        But there’s one gap in my understanding now. Languages like Haskell need monads to express things with side-effects like IO (right?). What’s unique about a monad that allows the expression of side effects in these languages?

                                                                        1. 7

                                                                          No problem!

                                                                          This is also why Rust “can’t express monads”, we can have instances of individual monads, but can’t express the higher concept of monads themselves. For that, we’d need a way to talk about “the type of a type”, which is another phrasing for “higher minded types”.

                                                                          So, originally, Haskell didn’t have monads, and IO was done another way. So it’s not required. But, I am about to board a flight, so my answer will have to wait a bit. Maybe someone else will chime in too.

                                                                          1. 2

                                                                            higher minded types

                                                                            (Just so others don’t get confused, I think you meant “kinded” here, right?)

                                                                            1. 1

                                                                              Heh, yes. Thanks.

                                                                          2. 3

                                                                            A monad has the ability to express sequence, which is useful for imperative programming. It’s not unique, e.g. you can write many imperative programs using just monoid, functor, applicative or many other tools.

                                                                            The useful function you get out of realising that IO forms a Monad is:

                                                                            (>>=) :: IO a -> (a -> IO b) -> IO b
                                                                            

                                                                            An example of using this function:

                                                                            getLine >>= putStrLn
                                                                            
                                                                            1. 4

                                                                              I should say Monad is unique in being able to express that line of code, but there’s many imperative programs which don’t need Monad. For example, just Semigroup can be used for things like this:

                                                                              putStrLn "Hello" <> putStrLn "World"
                                                                              

                                                                              Or we could read some stuff in with Applicative:

                                                                              data Person = Person { firstName :: String, lastName :: String }
                                                                              liftA2 Person getLine getLine
                                                                              

                                                                              So Monad isn’t about side-effects or imperative programming, it’s just that imperative programming has a useful Monad, among other things.

                                                                              1. 2

                                                                                You are way ahead of me here and I’m probably starting to look silly, but isn’t expressing sequence in imperative languages trivial?

                                                                                For example (Python):

                                                                                x = f.readline()
                                                                                print(x)
                                                                                

                                                                                x must be evaluated first because it is an argument of the second line. So sequence falls out of the hat.

                                                                                Perhaps in a language like Haskell where you have laziness, you can never be sure if you have guarantees of sequence, and that’s why a monad is more useful in that context? Even then, surely data dependencies somewhat impose an ordering to evaluation?

                                                                                For me, the utility of Steve’s and_then example wasn’t only about sequence, it was also about being able to (concisely) stop early if a None arose in the chain. That’s certainly useful.

                                                                                1. 2

                                                                                  but isn’t expressing sequence in imperative languages trivial?

                                                                                  Yes.

                                                                                  In Haskell it is too:

                                                                                  (>>=) :: IO a -> (a -> IO b) -> IO b
                                                                                  

                                                                                  But we generalise that function signature to Monad:

                                                                                  (>>=) :: Monad m => m a -> (a -> m b) -> m b
                                                                                  

                                                                                  We don’t have a built in idea of sequence. We just have functions like these. A generalisation which comes out is Monad. It just gives code reuse.

                                                                                  1. 1

                                                                                    Maybe is an instance of a monad, and there are many different kinds of monads. If you think of Maybe as “a monad that uses and_then for sequencing”, then “vanilla” sequencing can be seen as “a monad that uses id for sequencing” (and Promises in JavaScript can be seen as “a monad that uses Promise#flatMap for sequencing”).

                                                                                    Yes, expressing sequence in eager imperative languages is trivial because you can write statements one after the other. Now imagine a language where you have no statements, and instead everything is expressions. In this expression-only language, you can still express sequence by using data dependencies (you hit this nail right on the head). What would that look like? Probably something like this (in pseudo-JavaScript):

                                                                                    function (next2) {
                                                                                      (function (next) {
                                                                                        next(f.readline())
                                                                                      })(function (readline_result) {
                                                                                        next2(print(readline_result))
                                                                                      })
                                                                                    }
                                                                                    

                                                                                    with additional plumbing so that each following step has access to the variables bound in all steps before it (e.g. by passing a dictionary of in-scope variables). A monad captures the spirit of this, so instead of doing all the plumbing yourself, you choose a specific implementation of >>= that does your plumbing for you. The “vanilla” monad’s (this is not a real thing, I’m just making up this name to mean “plain old imperative sequences”) implementation of >>= just does argument plumbing for you, whereas the Maybe monad’s implementation of >>= also checks whether things are None, and the Promise monad’s implementation of >>= also calls Promise#then and flattens any nested promises for you.

                                                                                    What’s useful here is the idea that there is this set of data structures (i.e. monads) that capture different meanings of “sequencing”, and that they all have a similar interface (e.g. they have all an implementation of >>= and return with the same signature) so you can write functions that are generic over all of them.

                                                                                    Does that make sense?

                                                                                2. 2

                                                                                  There is a comment below saying it pretty succintly:

                                                                                  A monad is basically defined around the idea that we can’t always undo whatever we just did (…)

                                                                                  To make that concrete, readStuffFromDisk |> IO.andThen (\stuff -> printStuff stuff) - in the function after andThen, the “stuff” is made available to you - the function runs after the side effect happened. You can say it needed specific API and the concept of monads satisfies that API.

                                                                                  Modelling IO with monads allows you to run functions a -> IO b (take a pure value and do an effectful function on it). Compare that to functions like a -> b (Functor). These wouldn’t cut it - let’s say you’d read a String from the disk - you could then only convert it to another string but not do an additional effect.

                                                                                  EDIT: I might not have got the wording entirely right. I ommited a part of the type annotation that says the a comes an effect already. With Functor you could not reuse values that came from an effect; with Monad you can.

                                                                          1. 4

                                                                            Went to the National Gallery of Victoria (Melbourne, Australia), and saw some works of art that are on loan from MoMA in New York. Was very inspiring!

                                                                            Gonna do some more messing around on Pikelet. Trying to look at implicit arguments, and rework how I do variable binding. As you may notice I created an organisation for it last week!

                                                                            1. 4

                                                                              Moved my language project (dependent typed systems programming language) to a new Github organisation: https://github.com/pikelet-lang - been working up to a 0.1 release, which will sadly just be an interpreter. I am definitely aiming for unboxed data types and closures, linear types, compile time evaluation, and low level interop in order to win the right for the ‘systems language’ moniker though.

                                                                              1. 19

                                                                                While I do find Pony interesting from a technical perspective, I’ll admit that publishing every point release changelog to lobsters is an extremely generous understanding of what some of us would like the “release” tag to showcase.

                                                                                Major features? Sure. Security fixes, yes. But:

                                                                                Pony 0.23.0 is primarily a bug fix release but does also include some additional changes. Upgrading is recommended. There is a single breaking change caused by the implementation of RFC 56.

                                                                                is arguably less interesting.

                                                                                1. 4

                                                                                  Yeah, I’d be fine with a post when notable, interesting things have been added/removed/changed. Pony is an interesting language! But yeah, posting this one seems like it is wasting a bit of goodwill.

                                                                                  1. 2

                                                                                    as I write this, this story is hidden by 8, +10, -5 spam, which indicates to me that might be better to refrain unless there is a major milestone, or an interesting new feature that might spur some discussion.

                                                                                    Adding some additional commentary about why the breaking type change is interesting or useful might help; there are enough language implementation nerds who read lobsters regularly that it could generate some discussion, especially since the capability types are one of pony’s most compelling and novel features.

                                                                                    1. 4

                                                                                      I wont be posting releases to lobsters anymore.

                                                                                      1. 3

                                                                                        I don’t think anyone wants you to stop posting releases. They are just expecting more from each post.

                                                                                        1. 1

                                                                                          Perhaps. Either way. Not submitting them anymore.

                                                                                  1. 1

                                                                                    Does anyone know of a good introduction to property based testing?

                                                                                    1. 1

                                                                                      At what level? Lectures by the quickcheck guy are usually a good basic introduction.

                                                                                      1. 1

                                                                                        http://propertesting.com/ is a nice one, targeted at Erlang.

                                                                                        1. 1

                                                                                          This blog for the hypothesis Python library has a lot of great articles about how to use this stuff in “enterprise-y” software.

                                                                                          To be honest it was way more convincing to me than most other articles as to the utility of this stuff for higher-level applications

                                                                                        1. 4

                                                                                          Can anyone help me understand why Metal was designed? Apple’s a heavy hitter in Khronos, right? So what was it that they felt like they couldn’t accomplish with OGL/OCL? Are there non-Mac targets that support Metal?

                                                                                          1. 6

                                                                                            OpenGL is a tired old API that is too high level for high performance graphics work. At the time when Metal was being developed folks were working on lower level APIs to expose the GPU more, like Mantle and DirectX 12, and Metal was Apple’s offering. I believe Mantle eventually evolved into Vulkan, but for some reason Apple is continuing to promote Metal. It’s a nicer API for Swift users, but that’s about it. I would have preferred that they’d make a safe API over Vulkan for Swift like Vulkano, they seem to be under some weird impression that they’ll be able to trap devs in their platform with their own, proprietary API. Or maybe they just can’t bear to give up all the sunk cost.

                                                                                            1. 2

                                                                                              they seem to be under some weird impression that they’ll be able to trap devs in their platform with their own, proprietary API

                                                                                              Is it not working quite well for Microsoft with DirectX?

                                                                                            2. 1

                                                                                              As I vaguely recall, it started on ios as a way to utilize their graphics chips faster and more efficiently (lower overhead).

                                                                                            1. 5

                                                                                              Thankfully there’s MoltenVK, an implementation of the Vulkan API on top of Metal. Khronos has thankfully worked to get it open sourced for us! I’ve not used it before, so can’t claim it works as advertised, but if I was doing graphics work that’s where I’d head these days. Very sad that Apple continues to dig in its heels on this.

                                                                                              1. 3

                                                                                                Thought this was really neat. I’ve often thought that formal methods would be super handy on the front end, and likewise, I’ve also thought that formal methods folks could also learn a ton from UI folks. Great to see some work done towards bridging the gap!

                                                                                                1. 3

                                                                                                  One of the killer features of the Alloy specification language is that Daniel Jackson deeply cares about UIs. It makes using the IDE way more pleasant than any comparable FM.

                                                                                                  1. 1

                                                                                                    I previously submitted this work that applied formal methods to GUI. It’s more complex but integrates with Spin. The visual formalism in this article reminded me of what Tersus looked like years ago when I looked at it. I’m not sure what it looks like now.

                                                                                                  1. 2

                                                                                                    The whole thing is great, but one idea that seems particularly useful in arbitrary languages without regard to how it fits with other features is to specify the list of globals used by a function. In Python-like syntax, imagine this:

                                                                                                    def draw_quad(origin, left, up) [m]:
                                                                                                        m.Position3fv(origin - left - up)
                                                                                                        m.Position3fv(origin + left - up)
                                                                                                        m.Position3fv(origin + left + up)
                                                                                                        m.Position3fv(origin - left + up)
                                                                                                    

                                                                                                    Now you get a guarantee that the function uses zero globals besides m.

                                                                                                    1. 4

                                                                                                      In PHP you have something like that, global variables are not accesible from inside functions unless you specifically allow each one you want

                                                                                                      $m = new M();
                                                                                                      function draw_quad($orgin, $left, $up){
                                                                                                          global $m; // or $m = $_GLOBALS['m'];
                                                                                                          $m->Position3fv($origin - $left -$up);
                                                                                                          $m->Position3fv($origin + $left - $up);
                                                                                                          $m->Position3fv($origin + $left + $up);
                                                                                                          $m->Position3fv($origin - $left + $up);
                                                                                                      

                                                                                                      in practice, I haven’t found useful global variables other then the contextual ones ($_POST. $_GET, $_SESSION), which are superglobal and always defined

                                                                                                      1. 2

                                                                                                        I’d like to see something similar but generalised to “contextual” environmental bindings, rather than traditional global vars. And a compiler that ensures that somewhere in all call chains the binding exists. But you might want a global in some cases, or a “threadlocal”, or an “import” of some sort, or something like the context in react, etc.

                                                                                                        Some mechanism in which the compiler makes sure the environmental dependencies are fulfilled, without necessarily requiring that value be propagated explicitly through each owner/container between provider and consumer.

                                                                                                        1. 4

                                                                                                          I can’t find it but Scala has an extra context var that is passed with function invocation.

                                                                                                          And early Lisps had dynamic scope, meaning that a var bound to the next occurrence up the call stack.

                                                                                                          Both of these mechanisms supply the building blocks for AoP, so that a programmer can mixin orthogonal properties.

                                                                                                          1. 3

                                                                                                            And early Lisps had dynamic scope, meaning that a var bound to the next occurrence up the call stack.

                                                                                                            Today they still have it - see DEFVAR and DEFPARAMETER in Common Lisp.

                                                                                                            1. 2

                                                                                                              I can’t find it but Scala has an extra context var that is passed with function invocation.

                                                                                                              In Scala you can use implicit parameters for this:

                                                                                                              def foo(origin: Vec3, left: Vec3, up: Vec3)(implicit m: Context) {
                                                                                                                  m.Position3fv(origin - left - up)
                                                                                                                  m.Position3fv(origin + left - up)
                                                                                                                  m.Position3fv(origin + left + up)
                                                                                                                  m.Position3fv(origin - left + up)
                                                                                                              }
                                                                                                              

                                                                                                              In Haskell you could use a reader/writer/state monad thingy. In Koka or Eff you could use effects.

                                                                                                              1. 2

                                                                                                                Yeah, Scala’s context is probably closest to what I’m thinking of, from what I know of it.

                                                                                                              2. 4

                                                                                                                You can kinda get this with effect types. Effect types let you label certain functions as using resource A or B, and then you can have a documentation mechanism for what dependencies are used, without passing stuff around.

                                                                                                                It can still get a bit heavy (at least it is in Purescript), but less so than dependency injection

                                                                                                              3. 1

                                                                                                                A compiler or analyzer should be able to tell you that just from what variables or expressions go into the function. A previously-declared global would be one of the arguments. Why do we need to declare it again in the function definition?

                                                                                                                1. 1

                                                                                                                  See my final sentence. “Now you get a guarantee that the function uses zero globals besides m.” The program documents/communicates what it uses. The compiler ensures the documentation is always up to date.

                                                                                                                  In my example, m is not one of the arguments. Because m is a global, lexically accessible anyway in the body of the function. There’s no redundancy here.

                                                                                                                  1. 0

                                                                                                                    I’m saying a compiler pass should be able to do the same thing without a language feature. I think it won’t be important to that many people. So, it will probably be optional. If optional, better as a compiler pass or static analysis than a language feature. It might also be an easy analysis.

                                                                                                                    1. 3

                                                                                                                      You’re missing the point. It’s a list of variables that the code can access anyway. What would a compiler gain by analyzing what globals the function accesses?

                                                                                                                      There are many language features that help the compiler do its job. This isn’t one of them. The whole point is documentation.

                                                                                                                      (Yes, you could support disabling the feature, using say syntax like [*]. C++ has this. Just bear in mind that compilers also don’t require code comments, and yet comments are often a good idea. SImilarly, even languages with type inference encourage documenting types in function headers.)

                                                                                                                2. 1

                                                                                                                  What if the Position3fv method uses global variable n? You also need to specify that for draw_quad. This quickly blows up like checked exceptions in Java and people want shortcuts.

                                                                                                                  1. 2

                                                                                                                    The problem with checked exceptions is that they discourage people from using exceptions. But there’s no problem in discouraging people from using global variables.

                                                                                                                    I don’t mean to imply that I want all code to state what it needs all the time. In general I’m a pretty liberal programmer. It just seems like a good idea to give people the option to create “checkable documentation” about what globals a piece of code requires.

                                                                                                                1. 6

                                                                                                                  Sometimes I wonder why recent languages don’t support multiple [return] values. We are even using destructuring assignment as a poor man’s multiple values. It solves the problem described in the article w/o cluttering the functions code with conditionals

                                                                                                                  1. 2

                                                                                                                    Most recent languages honestly do (Python, Elixir, Go, Rust, Kotlin, and Swift (I think) to name a few), and they’re getting backported in some others (C# 7 and C++17, for example) by improving tuples.

                                                                                                                    I think the bigger issue with most of these languages is they don’t have anything like ML/Rust/Erlang-style fail-if-the-return-looks-like-this mode that lets you effectively use them as if they were exceptions when you want—e.g., the frequent pattern in Erlang/Elixir of Foo, ok = some_fun(), or Rust’s let v = bar()?;. Without that, you either do what Ruby’s doing here (throw an exception if exception: true is passed), or you have to explicitly check yourself (where Go is the extreme example of if foo, err := bar(); err != nil { ... } being a constant refrain).

                                                                                                                    1. 1

                                                                                                                      I don’t now about Elixir, Go or Kotlin, but neither Python or Go support multiple-values. In Go you have to accept to assign all the values the function returns, which kinda defeats the whole purpose of having multiple return values in the first place! (I checked in a REPL just to be sure). I haven’t kept up with recent developments in Python3 but AFAIK Pythonistas use lists or dictionaries to mimic multiple-values. Similarly in ES6 people use dictionaries + destructuring assignment in ES6. For Example

                                                                                                                      const foo = () => ({a: 1, b: 2})
                                                                                                                      
                                                                                                                      const {a} = foo()
                                                                                                                      
                                                                                                                      console.log(a)
                                                                                                                      

                                                                                                                      Prints 1. The ES6 approach is better than Golangs and Python solution but it still forces the caller to reuse the name the callee decided on[0] plus all the return values are allocated on the stack.

                                                                                                                      AFAICT Rust again doesn’t support multiple value but uses pattern matching (which destructuring assignment is one example of) to mimic them.

                                                                                                                      Multiple values are useful for more than exception handling btw, one example is the division operator

                                                                                                                      [0]: Yeah I know one can rebind the name, but the syntax is cumbersome to use and one still have to remember the name of the value as opposed to its purpose, which is easier to remember).

                                                                                                                      1. 2

                                                                                                                        Well, now I’m confused. On the one hand, you say,

                                                                                                                        …neither Python [n]or Go support multiple-values

                                                                                                                        …and then immediately link to a REPL that starts with “Go supports multiple return values,” and even shows an example of ignoring some of the returned values. Likewise, while I agree with you that Python uses sequences for multiple returns behind-the-scenes, the use in practice looks like

                                                                                                                        def foo(a, b): return b, a
                                                                                                                        c, d = foo(1, 2)
                                                                                                                        

                                                                                                                        which is indistinguishable in practice from what I do in an ML. (And in Python 3.5 and later, you can even do things like a, b, *rest = foo().) Python even has multiple-return division operations (see math.fmod from as far back as Python 2.7).

                                                                                                                        Could you give an example of a language that does what you want, and how it differs from the previous? I feel as if we’re using the same words, but for radically different things.

                                                                                                                        1. 2

                                                                                                                          I’m sorry I wasn’t more clear in my previous reply.

                                                                                                                          Could you give an example of a language that does what you want, and how it differs from the previous?

                                                                                                                          Common Lisp.

                                                                                                                          and how it differs from the previous?

                                                                                                                          • The caller doesn’t have to be aware that the callee supports multiple values unless they want to use it.
                                                                                                                          • The compiler can determine at the call site how many values are going to be used and allocate appropriately.

                                                                                                                          which is indistinguishable in practice from what I do in an ML.

                                                                                                                          Yes, and to the best of my knowledge MLs don’t support multiple values. Pattern matching enables one to mimic multiple values, which is what most recent language are doing and it provides at least 90% of the value of having multiple values.

                                                                                                                          The Go REPL example shows that the caller must be aware of the amount of values the callee is returning, which leads to clumsy UX. I’m guessing you wanted to link to modf but I’m not sure what you were getting at. The CL example of / shows how we can re use the same function instead of having mod and div. Similarly be taken advantage retrieving the value in a map where nil indicates absence without affecting the UX of the ‘happy path’ (ie. nil is not possible a value in the map).

                                                                                                                          –––––––

                                                                                                                          It is kinda ironic that my comment was motivated out of the idea of less is more in language design, in light of all the feature creep I’m seeing recently in JS and Ruby, but it appears that the approach of recent languages is better example of less is more ¯_(ツ)_/¯

                                                                                                                          1. 1

                                                                                                                            to the best of my knowledge MLs don’t support multiple values

                                                                                                                            Returning multiple values (like in Go) is just a special case of tuples, which most MLs support. So you don’t need an extra language feature for this. But I wouldn’t use this for errors like Go does - it makes more sense to use a variant/sum type for this.