A Programming Language for Games, talk #2

แชร์
ฝัง
  • เผยแพร่เมื่อ 3 ต.ค. 2024
  • Second talk on designing a programming language for games. Given on September 25, 2014.
    The first talk in this series is here: • Ideas about a new prog...
    This talk quotes from an email by John Carmack that is reproduced here in full: number-none.com...
    Casey Muratori's essay on Semantic Compression: mollyrocket.com...
    Mike Acton's cppcon keynote is here:
    • CppCon 2014: Mike Acto...

ความคิดเห็น • 254

  • @gameprojectX
    @gameprojectX 9 ปีที่แล้ว +137

    Gotta love that hard keystroke on slide change, as it expresses such resolve on the matter.

  • @Beefster09
    @Beefster09 10 ปีที่แล้ว +129

    I never realized how massive the gap is between "mainstream" programming and game programming. It seems that "mainstream" programming is centered around the client-server architecture, whereas game programming is focused on a high-performance single-machine architecture. Servers have to be reliable and maintainable. Games do not. They ship once and maybe get patched occasionally, but they aren't meant to run constantly like a server is. If a server needs better performance, you have the option of slapping in more processors. For a game, you can't expect your players to slap in another CPU or GPU. The development cycle for a game is a few years, whereas a server might be developed, maintained, and extended for decades.
    Since almost everything these days is in a client/server architecture, it naturally follows that the programming languages are evolving to meet the needs of those applications. And so you get safety, garbage collection, and whatever else. GC is a lifesaver for servers, as you will never have to worry about memory leaks that crash the server periodically. It's a huge problem for games, because you can't afford to have random slowdowns when the GC decides to do its thing.
    Different domains. Different applications. Different programming languages. Perhaps Bjarne Stroustrup was wrong in creating a "multi-purpose" language. There really is no "one size fits all" programming language, and there never will be. So we need to stop trying to make one.

    • @frencheneesz
      @frencheneesz 5 ปีที่แล้ว +27

      > [with GC] you will never have to worry about memory leaks that crash the server periodically
      You might have to worry less about that, but you certainly still get memory leaks. Just not the kind where nothing has a reference to your object. You can absolutely build up a bunch of garbage because your program isn't properly unlinking references to that garbage.

    • @jacksonstone693
      @jacksonstone693 5 ปีที่แล้ว +7

      With GC you have to start worrying about when the GC misbehaves and through song and dance trying to make sure your program performs well in spite of it if you have some kind of memory load. Have had times where GC freezes server so long external monitors think the service has crashed/gotten in a bad state and kill it for example. Basically I think you trade one set of issues for another set of issues.

  • @guilhermevieira8053
    @guilhermevieira8053 8 ปีที่แล้ว +16

    Oh gosh, I agree with almost every single thing you've said in this video and the previous one. I'm so glad someone else is working on these things!

  • @JasonBock
    @JasonBock 7 ปีที่แล้ว +21

    Just watched the first video and now this one and I find the talks fascinating, but I really like how much authority and force Jonathan uses to press a key to move to the next slide :).

  • @JakobQ42
    @JakobQ42 10 ปีที่แล้ว +57

    These are some really interesting thoughts Jonathan. I'm really sold on this new language your envisioning.
    Keep the talks comming. (^:

    • @yotty97
      @yotty97 10 ปีที่แล้ว +5

      you're not your.

    • @seditt5146
      @seditt5146 4 ปีที่แล้ว +3

      @@yotty97 I know I am five years late but still want to remind you that people such as yourself are a waste of all of our time including your own. Grammar nazis almost always totally miss the point of what people are saying trying to nitpick the stupid insignificant stuff so do yourself a favor and stop stressing you're , your or the proper usage of there because you are doing no one a favor. If you can understand what someone means in a clear enough fashion nitpicking what someone typed in a comment off the top of their head is dumber then any misuse or misspelling of a word.

    • @yotty97
      @yotty97 4 ปีที่แล้ว +2

      @@seditt5146 I didn't bother to read what you wrote but im sure it's dumb

    • @seditt5146
      @seditt5146 4 ปีที่แล้ว +1

      @@yotty97 Yeah neat, not only are you a picky dumb ass but you are obviously a liar as well, not shocked at all really.

    • @seditt5146
      @seditt5146 4 ปีที่แล้ว +1

      @@yotty97 Just take the advice that no one likes or is impressed by your grammar Nazi skills and move on my man.

  • @user-tt4dy1ti5v
    @user-tt4dy1ti5v 3 ปีที่แล้ว +7

    Q&A
    1:16:22 Why not use contracts instead of captures?
    1:17:14 Chandler Carruth, "Zero-Cost Abstractions and Future Directions for Modern Optimizing Compilers", LLVM Developers' Meeting talk, 2012.
    1:18:28 Do you think that the code maturation cycle could be applied to memory and type, e.g. GC until you specify how you control the memory?
    1:20:04 How to make memory management errors easier to fix?
    1:20:20 How would templates, generics and function overloading work given this syntax?
    1:21:49 Thoughts on controlled memory management such as arenas?
    1:22:31 Would you have built-in types like Vector3 and Matrix4?
    1:22:53 (I think coroutines are super-useful.)
    1:23:20 Thoughts on namespaces and modules?
    1:25:05 What would be included in the standard library given this proposed language is for games?
    1:26:15 (My datetime module in D was a single 30,000-line file.)
    1:26:31 "My biggest function in the Witness is like 14,000 lines, I think."
    1:26:43 How do captures differ from call-by-reference and restricting scope access? (I think that captures should be function arguments.)
    1:28:15 Have you given thought to using compile-time interpretation of code to generate generics?
    1:28:30 Were named parameters discussed in this talk?
    1:28:35 Should the compiler make available the memory layouts of data structures (e.g. to allow tighter game tooling)?
    1:29:50 [Closing remarks]

  • @soulstudiosmusic
    @soulstudiosmusic 10 ปีที่แล้ว +26

    Thanks once again for this - it's great unlearning the stuff I intuitively felt to be bullshit but had been programmed (sic) to do by default. Learning more by unlearning.

  • @chromosundrift
    @chromosundrift 8 ปีที่แล้ว +7

    I think it's really good to discuss programming language features in the context of what is good for games and how the balance point of cost/benefit tradeoffs might be different here.
    There is a real problem of "always do it like this" and "best practice" in software development wisdom. Things always depend and true wisdom is knowing what things depend on for your situation.
    Programming language design is tricky because you ideally want to enable programmers to decide their own tradeoffs (so your language is useful) and yet you want to make "right things easy" so you can't be entirely unopinionated.

  • @adeiasd1
    @adeiasd1 10 ปีที่แล้ว +13

    Your openness and sincerity are very inspiring.
    Please contiune posting videos and inspiring people. Keep being awesome :)

  • @KhadaKuraki
    @KhadaKuraki 10 ปีที่แล้ว +6

    Very interesting and enjoyable. Looking forward to the rest of the talks. Thank you.

  • @PHeMoX
    @PHeMoX 6 ปีที่แล้ว +7

    I agree with John Carmack when it comes to functions simply being as lengthy as required for whatever is supposed to happen in a sequential fashion. Honestly, I never understood why people need their code within two full windows, because while that might seem comfortable for programming itself it is of literal no usage to the game itself.

  • @benjaminlehmann
    @benjaminlehmann 10 ปีที่แล้ว +2

    Your points about 'the joy of programming' are valid. They are paralleled in Jony Ive's philosophy - a drive toward simplicity typifying good product design.

  • @dgmsstuff
    @dgmsstuff 10 ปีที่แล้ว +8

    John Carmack writes really well, not only code.

    • @Maraus92
      @Maraus92 4 ปีที่แล้ว

      I know this comment is 5 years old, but can someone point me to the letters written by John Carmack? I would greatly appreciate it!

    • @UncleGus007
      @UncleGus007 4 ปีที่แล้ว

      Everything John Carmack does is fascinating. I could listen to him talk to me about shit I don't even understand all day.

    • @MurtagBY
      @MurtagBY 3 ปีที่แล้ว +1

      @@Maraus92 number-none blog of John has a copy

  • @Pashb33
    @Pashb33 10 ปีที่แล้ว +2

    Great Talk Jonathan. I totally agree regarding calling of functions. This is one thing that really grinds my gears as a project grows in size.

  • @cgpilk
    @cgpilk 10 ปีที่แล้ว +3

    Great talks, can't wait for the rest.

  • @willmcpherson2
    @willmcpherson2 5 ปีที่แล้ว +10

    Being against the "many small functions" approach kind of took me by surprise, but I find myself agreeing. One thing I've found since taking this approach is that I have to decide between having complicated functional calls to pass mutable variables or moving variables to the outer scope. These are both really bad. In C, if you have a function with lots of mutable variables and you decide to split it up into 2 functions, you will end up either passing lots of pointers (and then dereferencing a bunch of pointers) or having a long list of variables in the outer scope which are now exposed to the entire file.

  • @ochuspokus
    @ochuspokus 10 ปีที่แล้ว +7

    When you think about it, syntax is really everything. If it isn't important, then why are there so many different languages? The syntax is an extrapolation of the thought process, and so it constrains your thinking. I've been an ardent C programmer my entire life. I used to program in C++, but switched to C, because I just can't stand object-oriented programming anymore, and I don't think that it is the best direction to go. After trying Haskell earlier this year, my mind was blown once I grasped how incredible functional languages are. You see, I was one of those imperative programmers that just saw lisp as some bizarre language that couldn't possibly offer anything new. How wrong I was. It's not just that better languages are more concise, but that they offer a new way of thinking about problems, and a new way to express solutions that are easier to read, comprehend and maintain. Therefore it would not be a stretch to say that the syntax IS your language, and is hence your way of thinking.

    • @spartacuspro88
      @spartacuspro88 9 ปีที่แล้ว +1

      But syntax is not the only thing that differs haskell from C++. You are basically saying that syntax is the most important thing that makes languages different which is wrong

  • @IndellableHatesHandles
    @IndellableHatesHandles 2 หลายเดือนก่อน

    I always define C++ lambdas at the top of the function and then call them at the bottom. This means that even when I capture everything by reference, the only thing that I end up capturing is the parent function's parameters.

  • @DanielSimmonsAu
    @DanielSimmonsAu 10 ปีที่แล้ว +6

    I felt the function assignment syntax was a bit inconsistent at around 48:00. To be consistent with the variable assignment, the type really should come between the colon and the equals. Also I feel the parameter name is part of the "function value" not the "function type". So I think it should have been more like this:
    // Declare and assign together
    square : float -> float = x -> {
    return x * x;
    };
    // Declare and assign separately
    square2 : float -> float;
    square2 = x -> {
    return x * x;
    };
    // Takes function as parameter (f is the name of the parameter with function type)
    some_func : (float -> float) -> void;
    some_func = f -> {
    // ...
    };
    // Call with previously defined functions
    some_func(square);
    some_func(square2);
    // Call with anonymous function
    // Parameter type is specified in declaration of some_func so no need to specify here
    // Just like you don't specify the type of a numeric litteral when passing as an argument
    some_func(x -> {
    return x * x;
    });
    Really enjoying these talks! Cheers!

    • @RobinsonFarrar
      @RobinsonFarrar 10 ปีที่แล้ว +4

      I mostly agree with this, but I think named variables are helpful instead of just relying on order.
      If I have a function of 4 or 5 variables, having line up
      f : (float, int, struct1, int, vector3) -> object1 = x, y, s, z, v -> { ... }
      It's harder to parse what y or z is.
      I don't really have an alternative to suggest, I just don't think you quite have it.

    • @DanielSimmonsAu
      @DanielSimmonsAu 10 ปีที่แล้ว +3

      Indeed. I agree!

    • @gameprojectX
      @gameprojectX 9 ปีที่แล้ว

      Robinson Farrar True! However, I think Daniel did have a point as well, there was something off with the type and assignment usage with functions compared to the data element shown before. Unless I misunderstood.

    • @RobinsonFarrar
      @RobinsonFarrar 9 ปีที่แล้ว

      I agree that there's something missing, but seeing a variable with type ((float -> float), float) -> float would be annoying as hell to read. Maybe the aliasing for different parts of a type could be standard. For example if you had built-in tuples (unlikely that he'll do this?) you could have a : (float v, string n) = (10.2, "Jeremiah");
      and then a.v would alias 10.2. That might make converting a temporary tuple to a struct annoying, though.

  • @Drachensingsang
    @Drachensingsang 8 ปีที่แล้ว +7

    I love sean's declaration syntax. It is exactly the same mathematicians use.

  • @drewm1980
    @drewm1980 9 ปีที่แล้ว +4

    I really like your ideas on making the syntax for closures and functions friendly for refactoring, and would love it they get implemented in Rust. Have you completely given up on writing an RFC and pushing an implementation through?

  • @turtlemasterroshi
    @turtlemasterroshi 7 ปีที่แล้ว +6

    As an up and coming game developer, I'm constantly feeling guilty for things like large functions and using globals. This video made me feel a lot better. ^_^

    • @jaysistar2711
      @jaysistar2711 5 ปีที่แล้ว +2

      If globals were not nessisary, the singleton pattern wouldn't exist.

  • @DeusExAstra
    @DeusExAstra 7 ปีที่แล้ว +2

    Some good ideas here... definitely wish C++ had many of these features.

  • @warlord1981nl
    @warlord1981nl 8 ปีที่แล้ว +3

    The section about declarations @ 29:00 is pretty much the way to do it in Delphi (~Object Pascal). 49:20 Syntax is everything to me hence I program in Delphi because I have tried C++ and I found it an atrocious language to work in. Delphi is very enjoyable to me syntax-wise, has it's own issues BUT I like programming in Delphi MUCH better than C++ for reasons amongst others being the syntax.

  • @bra5081
    @bra5081 4 ปีที่แล้ว +3

    As much as I highly regard John Carmack's work and opinion. You have to be a very good developer to write a multi page function that is readable. Because my experience has taught that doing that usually just turns the code into a big mess.

    • @marcossidoruk8033
      @marcossidoruk8033 ปีที่แล้ว

      Not really, that is only true if the control flow is really complicated in wich case you should try to simply it.
      If you can't simplify it that is generally a sign you have to rethink the problem and break it in simpler subproblems.
      Also breaking a function into smaller functions is only really bad when the functions mutate state, if they are pure functions there is no big problem in writing separate smaller functions as long as you don't abuse.

  • @willmcpherson2
    @willmcpherson2 5 ปีที่แล้ว

    The capture block idea is awesome

  • @theinsekt
    @theinsekt 10 ปีที่แล้ว +4

    The problems with function definitions like this:
    square := (x: float) -> float { return x*x }
    Is that it's more difficult to gues the meaning for someone that does not know the language. It might be better to use:
    square := function (x: float) -> float { return x*x }
    One of the reasons why python is easier to understand is that it often uses understandable keywords instead of cryptic symbols. Short understandable keywords might sometimes/often be a better alternative than cryptic symbols. It's often so easy to understand that a non python programmer can get the gist of what the code is trying to do.

  • @RyanPowell-t8s
    @RyanPowell-t8s 10 หลายเดือนก่อน

    i love the analogies to the aerospace industry

  • @oconnor663
    @oconnor663 7 ปีที่แล้ว +1

    The comments about Rust at 39min aren't quite correct. (Though it's possible things were different when the video was made?) You *can* use exactly the same syntax to declare a function inline as you do to declare it in the global scope. Example: is.gd/upLvmI The lambda syntax with the vertical pipes comes in when you want to declare a closure that captures local variables. There are a few different ways to do that, both inferred and explicit, and there ends up being some upsides to distinguishing that from regular functions that just take regular arguments. Also note: the vertical pipe lambdas are allowed to take curly braces like regular functions do, just not required to.

  • @HiAdrian
    @HiAdrian 10 ปีที่แล้ว

    Another great talk, I'm eager to hear more on this. Your syntax examples seem very appealing, not just for games. Raw pointers give me wet dreams.

  • @jaimerios3684
    @jaimerios3684 10 ปีที่แล้ว +1

    The presentation by Mike Acton at CPPCON is posted on TH-cam at: CppCon 2014: Mike Acton "Data-Oriented Design and C++"

  • @MixturaLife
    @MixturaLife 6 หลายเดือนก่อน

    12:00 functional approach and good types solves the second point

  • @dlwatib
    @dlwatib 5 ปีที่แล้ว +1

    Regarding captures: There's no good reason to have to declare captures at all. The compiler can infer automatically which variables are being captured, and most languages that support local procedures and/or lambdas do it that way. C++ is an outlier.
    That said, I do see the value of an optional capture as you specify it.

    • @jaysistar2711
      @jaysistar2711 5 ปีที่แล้ว

      I don't think that he was suggesting it as a requirement. In fact, I think that he was suggesing that it shouldn't be required because it could make your development cycle much longer to have put them everywhere, but he still is asserting that captures have value more than just for lambdas; they should be allowable on every block, so that one could definativly know if a block used any state outside of its scope. Adding them before release, but after development is done gives one peace of mind that their understanding of state access is actually how state is accessed.

    • @roxferesr
      @roxferesr 4 ปีที่แล้ว

      I agreed with everything he said in this and previous video.. except for the ‘capture’ bit. The usage proposed for captures (refactoring a local function out) doesn’t convince me.
      Having Good error messages from the compiler would be enough imo. The other advantages can be achieved by just using visibility modifiers and arguments
      .
      My argument is The visibility of global state should be handled by their respective owner. Not each individual function.

    • @MarkoMikulicic
      @MarkoMikulicic 2 ปีที่แล้ว

      @@roxferesr I think this is an example of "big idea language" vs "incremental straight-jacket" approach proposed in this series. Deciding the blast radius of global variables only via their visibility across compilation units assumes you must move your code around in order to accomplish this "perfect placement". This, according to the thesis in this series, reduces the joy of programming. The counter-proposal offers to solve the problem by moving the scope guard closer to where it's used.

  • @the_primal_instinct
    @the_primal_instinct 3 ปีที่แล้ว +1

    My problem with locally scoped functions is if you have 5 people working on the project someone who might wanna change it's scope to global might not even realise it exists.

  • @abysmallytall
    @abysmallytall 10 ปีที่แล้ว +1

    In case the Mike Acton presentation video he references doesn't show up in your sidebar. CppCon 2014: Mike Acton "Data-Oriented Design and C++"

  • @dmitrykolesnikovich
    @dmitrykolesnikovich 7 ปีที่แล้ว

    I wish you good luck, Jonathan

  • @kazriko
    @kazriko 6 ปีที่แล้ว +1

    One thing that I would be interested in. In the last talk you had joint memory allocation on arrays. As I understand it, arrays are very important for game development, partly because of Vector Processing as is done on graphics cards and other cpu vector extensions. You need the data you're operating on to be tightly packed together so that you can process it 4, 8, 16 or more times as fast in the leaf code using these vector processors. But when you're dealing with your higher level code, perhaps you might want to just deal with an individual character in the game. With arrays, you have to go struct.playerhp[playerid] and struct.playerarmor[playerid] and when you pass around characters to work with, you have to do the same, passing both the struct and the index in the struct. Do you think there would be a use for a language construct that would allow you to say player = struct[playerid] then access it as player.playerhp, player.playerarmor, even though these are not adjacent in memory?

    • @etodemerzel2627
      @etodemerzel2627 3 ปีที่แล้ว

      Just make a separate type for the player where all components would be pointers? E.g. player = foo(struct, id); player.playerhp->bar.

  • @gyuhyunlee8119
    @gyuhyunlee8119 ปีที่แล้ว

    After 8 years, I just realized that he was doing this presentation in a some kind of office, maybe Thekla Inc office. For all this time, I thought he was in his house or something

  • @vladzhukov6116
    @vladzhukov6116 5 ปีที่แล้ว +1

    As far as I understood you are not totally correct about Rust functions and lambdas. Rust has different syntax for lambdas, but of course you can declare local function with just function syntax. And reason for lambdas have different syntax is that they can take some variables from their context. (you'll get a compiler error if you try to borrow/move context variable from a local function with suggestion to use lambda)

  • @UncleKalle
    @UncleKalle 3 ปีที่แล้ว +1

    "Code has a maturation cycle". I just recently got into programming (again(?)) and I am sorta at the beginning of everything and only use console outputs. For my project I built a "menu", so shown text, user can input a number and then next menu comes up. My first attempt was 40+ lines of code for outputting menutext and connecting the functions to be called depending on user input.
    This had a problem: When adding a new menupoint, my function grew bigger, it got kinda messy and confusing to look at, changes to menupoints would take many steps so that at the end the change would run correctly (for example: replacing a menupoint from top to bottom would need a change in the user-input-function so that the correct function would be called etc.).
    So I ended up working out a menusystem where each menupoint is an object holding the shown text and the function to be called if chosen.
    TBH this "refactoring" took me longer than I expected it to, but it turns out to be a very "bugfree" way and it takes less time to code the menus afterwards.

    • @nintendude794
      @nintendude794 2 ปีที่แล้ว

      How’s getting into programming (again) going?

  • @KANJICODER
    @KANJICODER 7 หลายเดือนก่อน

    @12:00 If the comment doesn't match the code , both the code and comment are wrong. If there are no comments , the code can never be wrong as it does exactly what you told it to do .

  • @autious
    @autious 9 ปีที่แล้ว +2

    I really like the capture syntax, it's one of the few things i really like about the lambdas in c++, the construction makes a lot of sense for non-GC programs.
    I also enjoy the extension that the capture becomes part of the block and not part of the functions declaration, but it makes me wonder, where will it be stored when constructing instances of the anonymous function? I'm thinking both mainly in the case of recursion.
    The capture can't be part of the declaration as that memory is generally shared between multiple instances. This leaves us with "somewhere on the heap" which always makes a game programmer shudder. Or we can place it on the stack and somehow tie it to the function object, but then the function object no longer has a set size meaning that we have to handle variably sized types...
    Has this been dealt with in any later videos and talks?

    • @jblow888
      @jblow888  9 ปีที่แล้ว +2

      Max Danielsson Not yet. Lately I am thinking that the capture syntax discussed here is not heavyweight enough for what one might really want to do in terms of scope restriction. But I haven't actually worked on these ideas yet.

    • @autious
      @autious 9 ปีที่แล้ว +1

      Jonathan Blow Ok, Understood. Thanks for taking the time to respond.
      I really think your ideas are in the right directions regarding what the game development world needs, many of your ideas resonate with my own experience in building games. The potential for something like C in the control of memory but with some extra modern concepts that fits within the general agenda of "high level assembly" is always going to be useful for as long as we have to consider actual hardware.
      That's why the capture idea is so interesting, it's so simple on the surface, and it seems like it might be a good fit, but there are potential complications that might migrate away from that performance control that we need, and if the language has too much of that, or even any, that we can't avoid, i believe many might categorize the language as being "too high level" or "scripty". We have to be able to visualize what a specific piece of programming means for the assembly, memory access patterns, etc and shape that to create programs that the computer likes.
      C++ is such a pain to me, i sometimes have to be so selective about its features depending on my goals for my architecture. I've made attempts to be more of a purist and build things in C, and in some ways that "feels" more right and clean, but there are some things in C++ that i start to miss that really helps with productivity. The C++ toolbox just simply is bigger, even if it does contain things like screwdrivers with razor blades on the handle, it still has that nice inner core of C that we all know and trust. There must be some middle ground or a new direction that makes more sense for our field.

  • @soulstudiosmusic
    @soulstudiosmusic 10 ปีที่แล้ว +4

    Implicit types are a bad idea. Out of all the programming constructs I can think of, I can think of only one that is more dangerous - you don't always know what type a compiler is going to choose, and it gets really tricky really quickly. Although it may save a keystroke here and there, overall you're making your code more difficult to interpret by others, as they have to pursue the same interpretive logic that the compiler does, and ultimately you're writing less readable, less predictable code.
    While I can see the point of it purely for mocking up something in a hurry, really you're not saving much time. Certainly if you want to use that function for anything larger in any future scenario, you want your types to be explicit, not implicit, for the reasons stated above.
    Please note that scenarios such as declarations where type is specified by the type of the return value, are not true implicit type declarations (as the return type is declared explicitly) and templated functions are of course a completely different thing.

    • @jaysistar2711
      @jaysistar2711 5 ปีที่แล้ว +2

      Lots of newer languages have a fmt or formatter that you can run your code through (even your text editor can run it) to format your code. I don't see why part of this formatting tool, which is using the same internal representation of your code as the compiler, couldn't add explicit types to all implicit definitions.

    • @nintendude794
      @nintendude794 2 ปีที่แล้ว +1

      @@jaysistar2711 could or could not add?

    • @jaysistar2711
      @jaysistar2711 2 ปีที่แล้ว +1

      @@nintendude794 Thanks! I fixed it.

  • @jonseymourau
    @jonseymourau 8 ปีที่แล้ว +4

    How strong is the [[capture]] guarantee? If a function declared that way is passed another function as a parameter, it can't guarantee that function won't violate the guarantee since the guarantee is not part of the type definition.

  • @mybigbeak
    @mybigbeak 6 ปีที่แล้ว

    I really like the capture idea. It also lets you declare a function is "pure" with [ ]

    • @mybigbeak
      @mybigbeak 6 ปีที่แล้ว

      Oops, commented before you got there lol

  • @Keebus87
    @Keebus87 10 ปีที่แล้ว +15

    Interesting talk and topic, but your point with "const" is rather weak. Const has tons of benefits, and some languages like rust or D make it even more important with the additional level of constness, "immutable". Super important in any concurrent application.
    If at some point you say "Damn, I need access to a non const X from a location where only const X is available!" it means your design has some flaw. Const is crucial design tool to enforce correctness by design.

    • @shobbs72
      @shobbs72 10 ปีที่แล้ว +2

      I used to think the same way Jon does about const and I do still agree with him that repeatedly typing const everywhere is very tiresome. Having to type it twice on a single parameter in c++ is a bit nuts: "const int* const param". My own language requires lots of use of const in order for the automatic parallelism to work and it's one of my least favourite things about it. I did toy with making const the default and having to explicitly make things variable, but in the end it seemed like too big a departure from what people are used to. Now I'm starting to think I should re-consider.

    • @walter0bz
      @walter0bz 10 ปีที่แล้ว +9

      I'm a fan of Rust with immutable default, you just mark what's mutable . Const feels annoying in C++ because they got the default wrong. EDIT: Also Rust propagates immutable better .

    • @shobbs72
      @shobbs72 10 ปีที่แล้ว +1

      walter0bz I did read up on Rust a while back but I'll have another look, thanks. I don't like mutable as a keyword though - it's annoying to type :-)
      Edit: I see Rust uses 'mut'.

    • @massimotristano4277
      @massimotristano4277 10 ปีที่แล้ว +3

      walter0bz I agree with that. But it's not the const concept that is wrong in C++, const should be there, only flipped, thus removing the "const" keyword and adding a "mutable" keyword or something like that.

    • @David_Raab
      @David_Raab 10 ปีที่แล้ว

      shobbs72 Sure it is annoying to type. The language want you to encourage to think about a design that don't need mutable.

  • @Kenbomp
    @Kenbomp 6 หลายเดือนก่อน +1

    This talk is advance, you need years of experience to really understand this, but if you just follow it it works too

    • @MikAlexander
      @MikAlexander วันที่ผ่านมา

      I came to the same conclusion as they did, but by using a educational program called Construct ( thought it is very powerful ). Running on js engine, i noticed performance or rather frame time improvements the closer I was to that structure that is being described in the video and in the end landed on it.

  • @SpelMalmer
    @SpelMalmer 10 ปีที่แล้ว +1

    There are so many things about this talk that are just awesome. Love it!
    I would personally like it to be just slightly more "talky" instead of just symbols. That would increase readability and learnability aswell. Would also help syntax hilighting. Typing a few extra chars won't hurt.

  • @MorningNapalm
    @MorningNapalm 2 ปีที่แล้ว

    Nice talk, but I feel that what generally kills large projects, apart from changing requirements, is mostly flawed design and buggy implementations, not “friction”. Relaxing constraints on globals etc. is something which is primarily an option for very good, disciplined programmers, not for the programming populace in general.

  • @AshtonSnapp
    @AshtonSnapp 2 ปีที่แล้ว

    Personal idea as to coding best practices:
    If the code works, and it’s not a major pain in the ass to maintain, it’s good enough.

  • @walter0bz
    @walter0bz 10 ปีที่แล้ว +2

    What about lambdas with inferred return types. Some languages have lambda shortcuts (e.g. currying or #(...) in clojure).
    Rust is somewhere in the middle, e.g. |x|x*2 is only 3 characters of 'bloat' to make an expression a lambda
    in the rust community some people are still demanding further lambda sugar... but I think its' just right as it is now.
    The other reason Rust has different syntax is: a lambda is physically different to a regular function, its got captured variables. part of rusts' philosophy is against 'hidden costs' in both memory & time.

    • @drewm1980
      @drewm1980 10 ปีที่แล้ว

      I am staring at the rust Guide, and it is not obvious to me whether or not Rust could be extended to unify its functions and lambdas into one concept after 1.0. Jonathan Blow initially says he's unhappy that there are separate syntaxes for function-like parameters and captured parameters in Rust functions vs. lambdas, but the unified function/lambda he proposes also has separate syntax for the two types of parameters, so it's really the non-unification I think he's taking issue.

  • @kazriko
    @kazriko 6 ปีที่แล้ว +2

    12:00 Python has tests embedded in docstrings, which are basically comments that get run and tested. It's not really the most commonly used feature though.
    26:00 Actually, the Test Driven Development books agree with you on this a bit. Write your test, then Implement it in the simplest possible way to pass the test, which will often be cut and paste solutions, then once the test passes you factor it to reduce duplication.

  • @rdunnington2
    @rdunnington2 9 ปีที่แล้ว +1

    22:59 - Actually you don''t have to use lambdas to make a local function in C++. You can use a locally-scoped struct with a static function:
    void set_up_the_people()
    {
    struct Local
    {
    static void AddPerson(Array& people, const char* name)
    {
    auto character = new Character();
    character->name = copy_string(name);
    people.add(character);
    }
    };
    Array people;
    Local::AddPerson(people, "Mike");
    Local::AddPerson(people, "Leon");
    Local::AddPerson(people, "Josephine");
    }

    • @MarkoMikulicic
      @MarkoMikulicic 2 ปีที่แล้ว +1

      While you're at that you can add fields to the struct where you put your captured variables and instantiate the struct and call the now non-static method and here you have a closure (without heap allocation)

    • @rdunnington2
      @rdunnington2 2 ปีที่แล้ว

      @@MarkoMikulicic True, though in my experience the MSVC debugger isn't very good at finding symbols for those local structs, so it can be hard to debug.

    • @Henrik0x7F
      @Henrik0x7F 9 หลายเดือนก่อน

      @@MarkoMikulicic At that point you are just manually writing what the compiler will generate from a lambda

  • @roxferesr
    @roxferesr 4 ปีที่แล้ว +2

    For syntax reference regarding lambdas please check out Kotlin, I think they nailed it!

    • @janmatula1534
      @janmatula1534 4 ปีที่แล้ว +1

      agree, but you are a little late

  • @molewizard
    @molewizard 4 ปีที่แล้ว

    Applying a capture to any block is the most important thing in either of these two talks IMO. Had in the back of my mind the whole time that the biggest benefit of functions is they make the dependencies of the lines inside explicit. "These three lines *rely on these two variables*, and *you don't need to keep track of any local variables after they finish*. Long functions suck because any line could theoretically rely on or affect any other line, that's `N!` complexity. A 400-line function - what is affecting what? That's way too complicated. How do you get around that? Local functions work, but they're very verbose and the meaning is ambiguous, being a function implies the code is supposed to be re-used.
    But you pull each conceptual chunk into a block and use captures... Well, that's it. Solved.

  • @zaid-ajaj
    @zaid-ajaj 8 ปีที่แล้ว +4

    I disaggree on your point of putting the functions all together in one place. You say that this is "bad" because you now have to deal with multiple ideas when trying to understand the code and knowledge becomes implicit. I think it is actually THE OPPOSITE. You see when you move different pieces of functionality into there own NAMED function, you make your idea of what you are trying to do EXPLICIT and it shows how your original complex function was composed of smaller simpler pieces together and when reasoning about the code, you can do it in small simple pieces instead of a big chunk code that does something complex. Ofcourse this is not the only way of composing functionality nor the only way to enhace readability, it s often "good" to do so

    • @DavidDoolin
      @DavidDoolin 7 ปีที่แล้ว

      I have to agree, the rationale there was weak. Moving the expression of an idea (i.e., into function definition) doesn't change the number of ideas, it just changes where the idea is expressed.
      I get where he's coming from though. I've wrestled with the same problem writing scientific software, where the same sort of "programming school" truisms ring pretty hollow.
      There is a parallel conversation here, about testing, lurking just out of sight. I'll leave that sleeping dog lie.
      ----
      Ok, at 12:43 I see where he's going with this, and definitely agree with the specific point he's making there.

    • @Bunny99s
      @Bunny99s 5 ปีที่แล้ว +2

      Actually i think you didn't understand what he means by implicit / explicit knowledge. The knowledge wasn't about the purpose of a piece of code but the other points he made. So it was about where is that method used, what state is a sub method expecting, ... If all your objection is about telling the reader what a certain piece of code does, you can simply plug in a line comment before that piece of code which will label the next section. The benefits are that you aren't constraint to a singleCorrectlyLabelledIdentifier but are free to describe the functionality more clearly than with a method name. It's easier to follow the code since you can read it top to bottom and not have to jump around to fully understand what the method does. Code should be written self-explanatory in the first place.
      Apart from those points in time critical environments method calls can be slow. I've once written an image / texture rescaler in C# which does a simple bilinear sampling of the original texture. After inlining all method calls the code could be simplified by quite a bit. It was way better to avoid cache misses and run 8 times faster than the original code. Yes, it was a bit harder to understand (if you're doing several lerps manually inline it looks a bit confusing) but the overall length of the method was just a few lines longer but has everything embedded and was more simplified.

    • @swapode
      @swapode 5 ปีที่แล้ว +2

      I was about to disagree at first too. But actually he makes a good point and, unlike John Carmack, doesn't actually discourage factoring your code that way at all. It's more that the public interface of your code should be as lean as possible and exposing your partial functionality is probably a bad idea. You might later learn that something initially meant to be only partial functionality actually makes sense to be more widely accessible but that should be a deliberate step, so you move it from being a closure to being a global function or some scope in between.
      I don't think performance has to be an issue here. It's only about human perception. Ideally your compiler can automatically decide to inline the code, particularly with code that's only in a local scope, worst case you use a "inline" keyword to make that explicit.

  • @ruffianeo3418
    @ruffianeo3418 5 ปีที่แล้ว

    As for the first part (One big or many small functions), I would like to point out, that languages with nested functions solve that problem. With the extra benefit (if nested functions see the outer scope), that you can avoid passing all relevant and common arguments down to them. The next best thing is to have your MajorFunction() along with the MinorFunction(1..N)() in a separate module, where you only export the MajorFunction() (the Minors are static (inline) in C lingo).
    Many years ago I tried to use some other persons (student, probably) code, which was 1 program with 1 function (main()), which was around 10k lines of code. Guess what - I did not use it :)
    So yes, actually nested functions are one of the things which make functional languages sexy. Pascal not so much, in spite of it having nested functions :)

  • @DanielJamesCollier
    @DanielJamesCollier 7 ปีที่แล้ว +3

    C++ is like a movie that didn't care about continuity

  •  10 ปีที่แล้ว

    One thing about lambda and function pointer: lambda AREN'T function without name. They are objects of an anonymous type with a call operator method (which can be template overloaded in C++ 14). std::function is a polymorphic container that allow to contain either a function pointer or a lambda (or even a method pointer) and present it with a common interface that any function can called.
    BTW, lambda never allocate anything in the heap. It's a struct on the stack, whatever the closure's content is. If any allocation is done, it is done by the type of the closure members (like for string copy), but not by the lambda itself. In the case of std::function, it's its polymorphic nature that force it to use the heap (since it can contain objects of different sizes). Also, that's the big reason capture must be part of the type : the variable size is very different than one from a function pointer !
    Now, it would be nice to have a common accessor that allow you to make non-capture lambda compatible with function pointer.
    Edit: add note about the incompatibility between lambda and function pointers variables.

    • @armpitpuncher
      @armpitpuncher 10 ปีที่แล้ว

      A non-capture lambda *is* compatible with a function pointer.

  • @davidste60
    @davidste60 9 ปีที่แล้ว +1

    Generic functions: multiple functions for different data-types could be assigned to one variable. Avoids object mess.

  • @shobbs72
    @shobbs72 10 ปีที่แล้ว +1

    Seems like local function declarations will get you the bulk of what you're after. I think having to explicitly state all of the parameters every time you want to use a lambda sounds tiresome and prefer the C# way of having named delegate types which encapsulate the function signature in a single identifier. Not that C# then used that opportunity to make declaring anonymous methods easy, but they should have.

  • @walter0bz
    @walter0bz 10 ปีที่แล้ว +1

    1:24:00 ... I agree I really wish Rust kept elements "pub" by default, they used to have "pub" and "priv" keywords, but then on their minimalist drive decided to eliminate "priv", they did stats on their own compiler and made a judgement that everyone MUST code their way :(
    safety critical fields could be marked priv (or even unsafe?), that would be better, IMO.

  • @nitinjohnraj2544
    @nitinjohnraj2544 5 ปีที่แล้ว +1

    Yes, I'm 4 years too late, but I feel the need to point out that the `x` in `(x: float) -> float` seems totally unnecessary for the function *signature*, though it's obviously needed for the actual definition. I understand your argument for consistency, but I feel that throwing x into the function type just confuses things. I haven't seen JAI yet, though, but I hope that was pointed out before this.

    • @FaleneLogics
      @FaleneLogics 4 ปีที่แล้ว +2

      I suppose you are right if coming at it from type theory... but giving some parameter a name in a function declaration goes a long way towards self-documenting the code, in a language designed for actually programming in it.
      Of course that 'x' here doesn't add much... but consider: "makeCircle :: (radius: float) -> Circle".
      That seems much, much clearer to me than "makeCircle :: (float) -> Circle", and much less a hassle than "makeCircleFromRadius :: (float) -> Circle".
      Now, if using that function signature in some higher-order construct (like... 'apply a transform with arity-1 to all elements in a container'... err... "map"?) you indeed have a point.
      But then, nothing really prevents such a language from *also* allowing "unnamed" parameters, as C++ functions declarations would also allow. In the case of Jai, though, maybe it could interfere with some of the polymorphic-types-stuff... and would jeopardize the now supported named-parameters-passing syntax (from caller site). Yet the function's signature in "map" could anyway define its own generic 'x' name (or none, if possible) ; and still match 'makeCircle' definition...
      Oh, well.
      m2c

  • @CNLohr
    @CNLohr 10 ปีที่แล้ว +1

    Though on your previous video struck very positively, I am concerned about the great multitude of additional "features" that are part of the language, rather than something that can be added above the language. Otherwise, people would need to spend enormous amounts of time getting used to this specific language, and its syntax, rather than just coding.

    • @CNLohr
      @CNLohr 10 ปีที่แล้ว +1

      I guess my frustration was it feels like there's a lot of things that you want to be part of the language, that I think are things that should be above the language. While you should probably be able to let people make threads, coroutines, types of lambda's, etc... It's okay if they need support a library to do it.

    • @CaseyBorders
      @CaseyBorders 10 ปีที่แล้ว +2

      CNLohr I disagree. I think these things need to be built in so that they can be made more efficient and both from a runtime perspective as well as a development time perspective. I don't want to have to manage support libraries or make sure I'm importing all the things I need when they are so fundamental to what I want a language to do. Also, if it's build directly into the language, we can get better error messages and debugging information since that will all be built right into the compiler.

    • @davidjackson6320
      @davidjackson6320 10 ปีที่แล้ว +5

      CNLohr I agree with Casey. Syntax is a tradeoff: you put in the effort to learn the syntax for some language construct so that it's faster to do... whatever it is later on. If there's a lot of syntax around things you never use, that sucks, because you spent that effort and got little to nothing out of it. But if the syntax provides quick access to features you use all the time, it's a huge win. It pays for itself.
      The domain of the language plays a huge role here. Jon thinks that threads, coroutines and lambdas should have syntax because he's designing the language around his use case. He uses them so often that it never really makes sense to *not* have them "imported."
      A simple example: there's a programming language called Inform that was designed specifically for creating text adventures (like Zork). Inform has native syntax for representing a "room," a discrete area of physical space described by some flavor text. That would be an absolutely ridiculous syntactical construct in C, but it's essential in Inform, where it's an absolutely core use case. So Jon is designing a language around a set of use cases involving memory management and concurrency; if most of the people using this language will need lambdas (for instance) most of the time, that's a great reason for them to be in the language.

    • @CNLohr
      @CNLohr 10 ปีที่แล้ว +2

      Both of you guys make good points. I do think lambdas, and the like need to have language features.
      From Jon's later videos, I see more fully just why these things need to be language constructs... And why another syntactical approach isn't that good, i.e. wrapping everything in things that look like (or are) macros. So, that's fair.
      I'm just always thinking about the lower end of things, trying to get more to bare-metal.

  • @BlitzToon
    @BlitzToon 6 ปีที่แล้ว

    Mr. Blow you should make this language. Your celebrity status leaves you in a good position to leverage this change. Thx

    • @Kniffel101
      @Kniffel101 6 ปีที่แล้ว +1

      He does and has done this for ~4 years now, why did you write this comment? :o

  • @Heater-v1.0.0
    @Heater-v1.0.0 8 หลายเดือนก่อน

    Did programming schools get worse since my only formal CS education in 1975? Back in the days when there was no OPP, no FP, no whatever, only "Structured Programming". I'm sure I recall that functions were presented as means of wrapping functionality that is used from multiple places in a program. That is to say for reuse, saving on duplicating code. There was no suggestion to split big code into functions for sake of keeping functions small for no clear reason.

    • @____uncompetative
      @____uncompetative 8 หลายเดือนก่อน

      I never had any issues with Global variables, PEEK, POKE and GOTO.

    • @Heater-v1.0.0
      @Heater-v1.0.0 8 หลายเดือนก่อน

      @@____uncompetative Ah... The glorious old times. To this day most of the code I write would be just fine if everything was global all the time.

  • @____uncompetative
    @____uncompetative 2 ปีที่แล้ว

    7:00 // MinorFunction1(); isn't a function as it is inside the void MajorFunction() { body where MajorFunction() isn't a function either as MajorFunction is a potentially side effecting procedure and not a pure function free from side effects, and because all procedures are potentially side effecting it doesn't matter if MajorFunction() isn't because the code that makes use of MajorFunction() can't rely on MajorFunction() being free from side effects (without examining its source code, and understanding it enough to verify for themselves that it contains no side effects, and even then a change to MajorFunction() in a collaborative project could make it go from no side effects to side effects or even side effects in some rare conditional cases depending on certain input values), so the body having a sequence of imperative expressions/statements is not a function as you can't invoke these MinorFunctions in isolation from each other, whereas you can if they were factored out as separate procedures. At least in that latter pattern (the College style you refer to), these procedures can be independently invoked separate from each other and have well defined inputs and outputs, rather than some _ad hoc_ visibility of locals visible in MajorFunction() or globals visible in the main program. A pure function should only see its parameters and global constants, such as "magic numbers" which are best put into a global constant rather than repeated throughout the source even if their value is deemed constant this constant could change if the requirements end up changing when making a version for another platform with different architecture. Pure functions should not even see anything from the outer scope unless they are defined as inner functions that are textually nested within the other function, but even then the default should be that their own local variables and parameter names occlude visibility of those same names in the outer one. I'm not a huge fan of *this* being a way to refer to the variables in this outer scope, and there are ways to hack systems that rely on *this* being loosely defined, so it is best avoided even if it will require the inner function having to have more parameters which are the variables within the enclosing scope you want it to see which it might not see normally due to occlusion. Indeed, not being able to see anything of the outer function (as would be the case with a function if implemented outside of a nested textual inner function) would be more pragmatic as it would allow a function to start off as an inner function (like a genuine MinorFunction1() defined within MajorFunction() at the end of MajorFunction()'s body, being invoked at the start of MajorFunction()'s body along with the other two numbered minor functions you are decomposing functionality into), and then be refactored into a function outside of any function (and therefore visible and invokable from anywhere and consequently having more utility, which implies the only reason for nested functions is to hide complexity of an API as the "services" something needs in order to do its work could be hidden in its own definition and not usable by anything else, then there may be an argument to allow it for closures, but I think Jon said he doesn't use closures, and I have never needed one, and very few of the languages I have surveyed have had inner functions, so it is possible that a simpler syntax, with no closure semantics, would be preferable, and have more utility as it would force all functions out into the open where their services could be used, even if it wasn't anticipated they would be used independently of the MajorFunction that composed them as sub functions. Phew.

    • @youtubeenjoyer1743
      @youtubeenjoyer1743 ปีที่แล้ว

      did you seriously think that i would read all this shit by you?

  • @woolfel
    @woolfel 3 ปีที่แล้ว

    The main problem with the education system is they teach the wrong things. I'm self-taught and learned to program by reading good and bad code. A better way of deciding when to externalize a chunk of code is to ask this question "is any other operation going to need this?" If the answer is no, then it's better to inline the code. If you find you have lots of code that does a similar thing with minor differences, you should take a step back to identify if refactoring is needed. Sometimes the answer is yes and sometimes it's no. Programming is an art.
    universities focus too much on algorithms, theory and concept. University professors generally suck at writing great code that's maintainable. best way to learn how to write great code is to read great code. thankfully there are examples in open source, but you do have to find it. 95% of the code open and closed sucks!

  • @shitheadjohnson2797
    @shitheadjohnson2797 2 ปีที่แล้ว

    I only code really little stuff at the moment, like pong and finite state machines, then you dont have to worry about anything they are so small.

  • @DjChronokun
    @DjChronokun 10 ปีที่แล้ว +17

    I find the on-going discussions about syntax somewhat grating, why is it we build nice visual scripting environments that reject invalid input but when it comes to our own tools for serious programming text is seen as the absolute height of sophistication in code representation =\
    Having a single keystroke for each available action in a given context such as defining a new function or adding a loop or a assignment statement would surely increase the joy of programming, seems code editors could learn a thing from the UI's of games and 3d software.

    • @Freelancerk1bbles
      @Freelancerk1bbles 10 ปีที่แล้ว

      Interesting, I'm reasonably confident that if you look at the file for those visual scripted uh... scripts, they would be in plain text(with some info about positioning maybe).
      So what's to stop someone from building a C++ IDE that represents code this way?

    • @BleuShan
      @BleuShan 10 ปีที่แล้ว +1

      Freelancerk1bbles Probably the sheer complexity of the C++ standard.

    • @DjChronokun
      @DjChronokun 10 ปีที่แล้ว +1

      Freelancerk1bbles It's possible they are plain text but most likely they're formatted for easy machine parsing for the sake of keeping the editor simple. A C++ IDE that works this way could be made but it wouldn't benefit at all from keeping C++ syntax, it's only the semantics of C++ that would (potentially) be worth preserving.
      ***** I think the syntax should be simplified completely but for the sake of simplifying development environments, we should use source files that are easy for our tools to read so that our tools can be simpler. I see no use in an editor allowing you to write syntactically invalid code just to have your compiler tell you it's rubbish, so why not have a source code format that actually reflects the structure of the program rather than just a string of letters that every tool has to re-parse?

    • @EsaKarjalainen
      @EsaKarjalainen 10 ปีที่แล้ว +1

      vimeo.com/36579366
      Something like that?

    • @BleuShan
      @BleuShan 10 ปีที่แล้ว

      ***** What he describes is a node base visual programming environment. These things are really elegant solutions when it comes to solve a specific well defined problem set, to prototype small things or to describe logic in a declarative manner. I usually dislike them for a number of reasons.

  • @charlesearp6133
    @charlesearp6133 4 ปีที่แล้ว

    Don't Modules solve 15:00? That and private methods / functions. The general premise does connect well to the misuse of OOP. I know that my intro to c++ classes taught the wrong way, to break up any function "longer than 5 lines", which inevitably leads to spaghetti code. This excessive focus on not repeating any logic, on code reuse, is disastrous, since it tends to split the logic over potentially multiple files, and makes it impossible to understand. A big function, that exposes a single interface, that hides all intermediate results, however ugly it may seem, is often a better solution. But with privacy, you can get reuse without allowing other code to depend on it, making it easier to refactor.

  • @Pyromuffin
    @Pyromuffin 10 ปีที่แล้ว +1

    what is the difference between a capture and an argument? By value vs by ref? If you're just reading a global variable, why couldn't you just pass it in as an argument?

    • @andreafiorito9491
      @andreafiorito9491 5 ปีที่แล้ว

      Capture works like the member in a functor. functor is an object that overloads operator ( ) and has a state (which is stored in his members). In c++ when you define the lambda is like executing the constructor of the functor and passing initialisation for the members to it.

  • @nikolaikalashnikov4253
    @nikolaikalashnikov4253 6 ปีที่แล้ว

    24:36 Quote: *now note that the syntax for the function declaration changed between when it was a lambda and when it was in global scope* Jonathan, I have read other people complain about this inconsistency before too. It didn't really bother me, but it begs an interesting question of "How do we design it to be more consistent ?" This is what I came up with so far...
    I call the syntax *Function Builder Notation*
    gist.github.com/Europia79/78bf804f7630a6a64ebf7cade1ce219e
    class Main {
    /**
    * main() function that demonstrates the syntax of Function Builder Notation
    *
    * .returns(void) would be optional since it doesn't actually return anything
    * .captures() is optional since it doesn't actually capture anything
    * .throws() is optional since it doesn't actually throw any Exceptions
    *
    * This notation attempts to provide a consistent way to declare
    * both global functions and locally scoped lambda functions.
    */
    fp main = fn(String[] args).returns(int).captures().throws()
    {
    // fp = function pointer
    fp lambda1 = fn() { }; // fn = pure function
    fp lambda2 = fx() { }; // fx = impure function with side effects
    fp lambda3 = fm() { }: // fm = function method with captured state
    fp lambda4 = fm() { }; // automatically captures all local variables
    fp lambda5 = fv() { }; // fv = virtual function with default behavior
    fp lambda6 = fv(); // virtual function with no default behavior
    return 0;
    } // End of method main
    // Example showing multiple return values
    fp example1 = fv().returns(int, int);
    // Example showing a function parameter & function return value
    fp example2 = fv(fp.r param1).returns(fp.r);
    } // End of class Main

    • @nikolaikalashnikov4253
      @nikolaikalashnikov4253 6 ปีที่แล้ว

      49:10 Nice, just prefix process_array 's second argument with a some keyword like *fn*, *fun*, or *function* (for readability), and you're golden... There also seems to be typos: Should probably be
      process_array := (array[] : float, x : (float)->float) { ... };
      (since we're just defining process_array, not actually calling it yet... it's body would call x() and pass a float to it... and when we call process_array, we'd pass a function as the second argument... process_array itself seems to be a void function: Or in other words, an operation on the first parameter: An array).
      Altho, I'd probably prefer
      *fp* process_array = *fn* (float[] nums, *fn* .r x) { ... };
      With body:
      fp process_array = fn(float[] nums, fn.r operation).returns(void)
      {
      for (int index = 0; index < nums.length; index++) {
      float n = nums[index];
      nums[index] = operation(n);
      }
      };
      fp no_op = fn(float f).returns(float)
      {
      // do nothing
      // no operation
      }
      fp increment = fn(float f).returns(float)
      {
      return f + 1;
      }
      fp square = fn(float f).returns(float)
      {
      return f * f;
      }
      Calling it:
      float[6] numbers = { 0, 1, 2, 3, 4, 5 };
      process_array(numbers, no_op);
      process_array(numbers, increment);
      process_array(numbers, square);
      process_array(numbers, fn(float f).r(float) { return f - 1; } );

    • @nikolaikalashnikov4253
      @nikolaikalashnikov4253 6 ปีที่แล้ว

      51:26 capture
      f := (x : float)->float [y] { return x * x + y };
      fp f = fn(float x).returns(float).captures(y) { return x * x + y };
      Sorry, I wasn't 100% sold on my idea of Function Builder Notation... so I wanted to refactor your method to C% notation & see them side by side.
      Great readability... debatable writeability... I can see a lot of people complaining about the extra keystrokes... but I personally don't mind the extra second or two.
      I'm actually loving the readability of it.
      Anyways, eventho I'm not a fan of your specific notation, I do greatly appreciate the goal of consistency.
      Awesome video !!!

    • @nikolaikalashnikov4253
      @nikolaikalashnikov4253 6 ปีที่แล้ว

      58:10 Ah, yes, your syntax has awesome consistency for also being used for blocks:
      Array people;
      [&people] {
      auto character = new Character();
      character->name = copy_string("Larry");
      people.add(character);
      }
      // In order for me to be consistent, mine would have be
      captures(people) {
      // ...
      }
      Yours is very nice & elegant... not sure how I feel about mine. It kinda looks like a function call. lol

    • @nikolaikalashnikov4253
      @nikolaikalashnikov4253 6 ปีที่แล้ว

      1:08:55 Okay, I was doing *fn* to denote pure functions and *fx* to denote impure functions with side-effects...
      Your double-bracket notation to denote pure functions is a nice idea.

  • @SeanMcBeth
    @SeanMcBeth 10 ปีที่แล้ว +2

    Do you have this available as a blog post instead of a video?

  • @firesoul453
    @firesoul453 8 ปีที่แล้ว +1

    Ok I know I'm late to the party but in the previous video you talk a little about how a new programming language would help prevent the problem where productivity slows due to growing program complexity over time. And here you started talking about how factoring out functions can be bad because it can increase complexity.
    But wouldn't Test Driven Development be the answer? That way your "comment" is now in a form a code that actually will run. Then your comment would be tested.

    • @jblow888
      @jblow888  8 ปีที่แล้ว +4

      TDD is yet another bad fad paradigm that doesn't solve any real problems, yet creates many new problems. In 10 years it will be gone and the next bad fad paradigm will be here.
      One problem with TDD (of many) is that it hugely increases the volume of code you have to write to build anything. As your question puts it ... okay, now your comments are code, but that means you are now responsible for maintaining massively more code than before, and taking the productivity hit of compiling and running that stuff all the time.

    • @firesoul453
      @firesoul453 8 ปีที่แล้ว +1

      It is a trade off for sure.
      I've probably been watching too many Uncle Bob videos.

    • @Drachensingsang
      @Drachensingsang 8 ปีที่แล้ว +2

      Uncle Bob works in a very different world, where tests can help, and functional programming can help, etc. But that is less about explorative programming which can often be favourable to (indie) game programmers, and rather about customer oriented solutions that fit predefined desired outcomes and have to be easily ported to different databases with only minimal change. Tests can really help with that, but games provide a vastly different environment. Porting to a different games platform means cuddeling with the memory. Tests won't do you any good.

    • @firesoul453
      @firesoul453 8 ปีที่แล้ว

      +Kolt Kloud good point.

  • @KilgoreTroutAsf
    @KilgoreTroutAsf 2 ปีที่แล้ว

    Coming from the HPC world I can confirm how many "programming best practices" just obfuscate the task and sharply decrease performance.
    If you are afraid of pointers, long functions and designing for performance maybe you should have chosen a different career.

  • @andrewkrieg538
    @andrewkrieg538 10 ปีที่แล้ว

    When are you going to upload the next talk and where do you stream this? I would like to watch the next one live. Thanks

  • @clperez32
    @clperez32 5 ปีที่แล้ว

    Maybe a bit late by know. But at minute 46:18. Lambda return values, maybe I would put the returning type at the like} : float. It would be consistent with the rest of typings in the language, and would indicate you can skip the typing

    • @clperez32
      @clperez32 5 ปีที่แล้ว

      Minute 52:58. By this time I think the one proposed in the video is better option

    • @clperez32
      @clperez32 5 ปีที่แล้ว

      59:27: fantastic summary

  • @o__sama
    @o__sama 10 ปีที่แล้ว +3

    what about c# syntax?, my idea is making a native compiler for c# using roslyn & llvm, & throwing away the .NET dependency. we can also add features if needed.

  • @nilssab
    @nilssab 10 ปีที่แล้ว

    I only have moderate experience with C++ and I'm not familiar with lamdas. I assume these are borrowed from lisp as Jonathan insinuated, but are these new in C++11 etc.? Also how do lamdas relate to a monad? (I'm not very familiar with functional programming either..)

  • @Lestibournes
    @Lestibournes 5 ปีที่แล้ว +2

    Typing arrows is annoying and I don't see why you need them.

    • @jpratt8676
      @jpratt8676 5 ปีที่แล้ว

      I would much prefer another colon instead of that arrow and also wish the arguments were next to the function call e.g.
      foo(x:Float, y:Float):Float = x+y
      Lambdas can still use this (with underscore as the anonymous name)
      (_(x:Float, y:Float):Float = x+y)

  • @mikumikudice
    @mikumikudice 2 ปีที่แล้ว

    1:09:00 indeed. That's pretty cool

  • @rismosch
    @rismosch ปีที่แล้ว

    46:28 jonathan blow smaks the shit out of his keyboard

  • @Canonfudder
    @Canonfudder 8 ปีที่แล้ว

    Could your language kick inversed defined statesets?
    stuff like:
    if (precise condition){
    }
    else { //do something} // here be only erroccases
    or if (i != -1 )
    were many always assume that nothing will change to the code, and assume all cases will remain correct.
    Also syntactic sugar request :
    if &&{ a==b;c==d;etc.} || { what== if; etc.}

  • @Js103036
    @Js103036 8 ปีที่แล้ว

    Hey Jon, I've been following you as a game designer since gears of war came out. Although I'm an artist, I'm trying learn programming. I took an introductory programming class relative to simple procedural programming in college before i graduated just to get some credits, I wanted to learn how to program for games, how can I be as proficient as you? Aside from syntax, where can I learn to program for games? Where can I learn the logic for coding games?

    • @SpeedfreakUK
      @SpeedfreakUK 3 ปีที่แล้ว

      Did you end up learning to code in the end?

    • @Js103036
      @Js103036 3 ปีที่แล้ว +1

      @@SpeedfreakUK Nope. xD
      I went to a visual editor called Bolt instead and this worked out amazing. 😆
      Seeing everything in code it too vague and monotonous for me. Its easy to get lost, easy to f***k up on the wrong or similar looking logic, and syntax on top of that is beyond annoying. I also don't come across videos that provide a sensible understanding of code logic and algorithms and as a result I almost never see progress; even when the logic seems correct, it always ends in frustration and a desire to give up. Using a visual editor, I hit the ground running in 30 mins. I was knocking out features like crazy. What should have worked in code, works perfectly fine in visual scripting. Now I have a prototype. Its still in development, but it shows promise. Unfortunately my 9 to 5 job cuts development time dramatically so I have to break things down to keep the ball rolling. Overall, it's looking alright. 👍

  • @etisdew2985
    @etisdew2985 7 ปีที่แล้ว

    48:15
    What do you mean with := type -> type {return...} & just = type...(exactly the same as the first), I think you're covering it... I dig the := can I; foo : type -> type = {...}

    • @jblow888
      @jblow888  7 ปีที่แล้ว

      Well, the lambda syntax includes the type so you'd have to write it twice in that case, which is not ideal!

    • @etisdew2985
      @etisdew2985 7 ปีที่แล้ว

      Hey, thanks for replying I have a massive amount to catch up on. Is there a repository for this? This feels like what I wanted of javascript.

    • @jblow888
      @jblow888  7 ปีที่แล้ว

      Not at this time!

  • @banama1758
    @banama1758 10 ปีที่แล้ว

    great videos

  • @sassafrasa
    @sassafrasa 6 ปีที่แล้ว +1

    23:00 You can define a locally scoped function in C++ by putting a static function in a locally defined class. void DoSomething() { struct Local { static void ReallyDoIt() { } }; Local::ReallyDoIt(); }

    • @charlesearp6133
      @charlesearp6133 4 ปีที่แล้ว

      I believe anonymous namespaces work as well.

  • @AdroSlice
    @AdroSlice 4 ปีที่แล้ว

    I don't like how you have ":" for the type of variables, but "->" for the return type of functions. I think that
    square := (x: float): float { return x*x; }
    is a perfectly reasonable syntax.

  • @hasen1957
    @hasen1957 6 ปีที่แล้ว

    Is there any transcript of this talk? Specifically the first 20 minutes or so. I think it would be valuable in text format.

    • @Kniffel101
      @Kniffel101 6 ปีที่แล้ว +1

      I don't believe there is.
      But you're free to make one, if you want to do the work.

  • @cthutu
    @cthutu 8 ปีที่แล้ว +2

    Although I agree with Jonathan Blow on many things, I think his lack of experience with scheme and lisp shows. He says that they cannot be used for real projects. This is totally untrue. One of the reasons he cites is dynamic typing. You can generate efficient code even with dynamic typing using type inference techniques and basic code analysis. Lisp-based languages have been used for very high-profile software, including video games (such as Jak & Daxter by Naughty Dog using their GOOL language). Scheme has a lot of symmetry that allows some very expressive and productive code writing, including be able to meta-compile that he rates so highly. I also disagree with the syntax making it hard to read. It takes a week to get to used to and you will find syntaxes for other languages overly complex. I will agree with your objections to garbage collection, but even then this field is improving very well.
    Also, lambdas in programming languages have been around since the 1950s (not 1960s) when John McCarthy first designed the first lisp, who also pioneered garbage collection.

    • @Heater-v1.0.0
      @Heater-v1.0.0 8 หลายเดือนก่อน

      I think you answered you own question there. As Johnathan stated Garbage Collection is a complete non-starter in many situations that demand performance and/or tight timing guarantees. As is often the case in games, real-time systems and many others. The syntax of Lisp and Scheme are of course very elegant but there is no way normal people are going to accept that for general programming.

    • @cthutu
      @cthutu 8 หลายเดือนก่อน

      @@Heater-v1.0.0 you can definitely do games with garbage collection. You pre-allocate. There's a few games written with lisp. Abuse is one, and naughty dog did a few too.

    • @Heater-v1.0.0
      @Heater-v1.0.0 8 หลายเดือนก่อน

      @@cthutu Indeed you can. You can also pre-allocate in C where it is a more natural, indeed normal, thing to do. Minimising memory allocations/deallocations is necessary for peak performance in all languages.
      The question then is: Why would one use one language over another? Why not choose a language that supports what you want to do more easily?

  • @cgpilk
    @cgpilk 10 ปีที่แล้ว

    Does C++ actually specify what the output code of a lambda should be? I would have thought a compiler could either inline it or pull it out into a separate function? (Basically like the "inline" keyword which is basically a suggestion at best)

    • @mperepelitsyn
      @mperepelitsyn 10 ปีที่แล้ว +1

      Yes, the standard specifies that. A lambda translates into a function object with data members corresponding to captured variables.

    • @armpitpuncher
      @armpitpuncher 10 ปีที่แล้ว

      Max Perepelitsyn
      It doesn't specify the output. It specifies what a lambda is equivalent to, in terms of function object classes. It can of course be inlined, and very often is, but so can a function object call.

    • @cgpilk
      @cgpilk 10 ปีที่แล้ว

      armpitpuncher Ahh great, that is what I was hoping for.

  • @MrConstant1n
    @MrConstant1n 10 ปีที่แล้ว +1

    if i declare a function like:
    square := (x: float) -> float { return x*x; };
    what "square" varyable type will be? a "function" type?
    So if i wrote something like:
    square : function = (x: float) -> float { return x*x; };
    or
    square: function;
    square = (x: float) -> float { return x*x; };
    will it be right?

    • @goranmilovano
      @goranmilovano 10 ปีที่แล้ว

      A function that takes a float, and returns a float, would have the following type:
      square: (float) -> float
      ... I think.

    • @somewhere-else
      @somewhere-else 10 ปีที่แล้ว

      ChocoRX1 As defined in many typeful functional languages (eg Haskell) he isn't being speculative, he is being factual. However, the point is that that IS a function type, it's just a more specific function type than you'd get out of, say, Javascript.

    • @somewhere-else
      @somewhere-else 10 ปีที่แล้ว +2

      ChocoRX1 No problem! Didn't mean to be accusatory or anything, genuinely trying to be helpful.

  • @Murderface666
    @Murderface666 10 ปีที่แล้ว

    OK, whats the modern way to make a language (tools, etc), because I just scrapped mine

  • @crimsonhawk52
    @crimsonhawk52 4 ปีที่แล้ว

    Pretty late watching this, but I have a question. Maybe I'm just screaming into the void though...
    At around 53:00, when Jon talks about how function syntax and how it should just be "tacking on" stuff and how changing the function from a full declaration and capture and the rest to JUST a declaration is as simple as putting a semicolon and deleting the rest,
    shouldn't the equals sign be right before the braces instead of after the colon?

  • @nikolaikalashnikov4253
    @nikolaikalashnikov4253 6 ปีที่แล้ว +1

    27:45 *declaration syntax* I'm not a big fan of this colon syntax that all these new languages are doing: Languages like Go, Rust, Kotlin, & Jai. This whole nefarious idea is rooted in SAVING KEYSTROKES !!! (via implicit types). However, I often find myself thinking in terms of 'building blocks'... so when I'm writing code, I think first & foremost in terms of "What do I need ?" (What type do I need). And also, when I go back and READ code... I find myself examining type information first. I don't know, maybe it's just because I'm so used to the syntax of the C-family of languages like C++, Java, & C#. But in order to placate all the keystroke saving warriors, I will suggest an alternative syntax:
    First, the problem syntax:
    HashMap pmap = new HashMap();
    Bad example, I know... but notice that the problem is that you type HashMap twice.
    Translated to Rust, Go, Kotlin, Jai, it would probably be something like
    pmap := new HashMap(); // No view of the interface
    // with a view of the interface:
    pmap : Map = new HashMap();
    I don't know about this... At first, I was thinking maybe prefix the interface to the implementation like
    pmap = new Map:HashMap();
    But this has the obvious problem when you declare without initializing.
    Then I thought about a possible postfix &/or object-notation:
    pmap = new HashMap().cast(Map);
    I really like this object-notation syntax, especially when paired with short-hand notation for Number types: integer, float, decimal, etc... but unfortunately, it has the same problem when you declare without initializing.
    Then I came full circle back to the original notation:
    Map pmap = new HashMap();
    Summary:
    It has GOOD WRITEABILITY. (How we think when we code).
    It has GOOD READABILITY. (Types are important... otherwise, we'd used a dynamically typed language).
    To elaborate on readability... when I'm looking at a method signature, I don't really care what the parameter names are... I care what the types are. Put the types first... not the names first.
    If you guys come up with a way to save keystrokes without sacrificing writeability or readability, then that's good... but the job of saving keystrokes is probably better left to IDEs.
    Anyways, at least I think I came up with a better casting notation. Compare:
    ((SubType)object).dispatchMethod();
    object.cast(SubType).dispatchMethod();

    • @naxaes7889
      @naxaes7889 5 ปีที่แล้ว

      I would disagree that it's about saving keystrokes, although it's nice that it does save keystrokes. I think you've misunderstood its use case. It's supposed to be a replacement for *auto* in C++ or *var* C#, and only used when the type isn't necessary as long as it follows the correct semantics (i.e. duck-typing), or is redundant.
      Now consider a more proper comparison. Which is cleaner?
      auto x = new Vector(); // C++
      var x = new Vector(); // C#
      x := new Vector();
      It's obvious that the type is Vector, so we don't have to specify the type. In C++ and C# we do however need to specify that we don't want to specify our type. Which is inconvenient.
      Now to your example. If we don't want a polymorphic type, then it's perfectly clear to write
      pmap := new HashMap();
      You're declaring and initializing a variable pmap that's of type HashMap. No ambiguity, less code, readable and writeable. Win-win.
      If you want a polymorphic type conforming to an interface, then you'll have to specify the interface.
      pmap : Map = new HashMap();
      So, what's actually wrong with this? Your argument is just _"I don't know about this..."_ even though it covers all the benefits you've listed. It explicitly lists the interface and the type and is both readable and writeable.
      This syntax is just as clear as the other syntax but can save type strokes and reduce redundant noise in certain situations.

    • @cemgecgel4284
      @cemgecgel4284 ปีที่แล้ว

      @@naxaes7889 To answer the first bit, cleanest to me is:
      Vector x{};
      Same allocation semantics:
      std::unique_ptr x{};
      You can get rid of the equal sign completely (Made up Java, C# syntax):
      Map pmap{new HashMap()};

    • @naxaes7889
      @naxaes7889 ปีที่แล้ว

      ​@@cemgecgel4284 Yes, that's one alternative. But now you've introduced something worse, inconsistency. It's all about trade-offs and using colon-equal instead of auto/var reduces typing and introduces no downsides.
      Your suggestion (the C++ way) reduces typing, but now there are multiple ways to declare a variable. This reduces readability, and searchability, and introduces complexity for analyzers needing to parse the program.

  • @larrytroxler7017
    @larrytroxler7017 7 ปีที่แล้ว

    Disclaimer: I'm watching this video as I type. I am not a game programmer. But isn't it obvious that game programming is all about performance? Most of programming suggestions, RAII, law of demeter, dependency injection, etc, are largely guidelines for general software that is rather large. I would think that game programming is a different animal. performance is an issue, and I always thought that most games involved both a physics library and an embedded meta language, often lisp based. is this not true in practice?

    • @etodemerzel2627
      @etodemerzel2627 3 ปีที่แล้ว

      Is that why the majority of modern software sucks?

    • @larrytroxler7017
      @larrytroxler7017 3 ปีที่แล้ว

      @@etodemerzel2627 what are talking about? Please explain your logic. I truly don't understand. I know the topic is about game programming, but then you said the majority of software sucks. I would hope that you will agree that majority of software is not games.

    • @etodemerzel2627
      @etodemerzel2627 3 ปีที่แล้ว

      ​@@larrytroxler7017 There's a somewhat unpopular opinion that modern software is really slow and bulky. And it seems to me you were implying that non-games don't care about performance?

  • @alan2here
    @alan2here 6 ปีที่แล้ว

    Inline
    func()
    {
    // some code
    {
    // more code
    }
    {
    // more code
    }
    }
    And then outline :)
    func()
    {
    // some code
    + ...
    + ... // some comment
    }

  • @andreffrosa
    @andreffrosa 9 หลายเดือนก่อน

    Where is the 3rd one?

    • @gusgus1330
      @gusgus1330 8 หลายเดือนก่อน

      th-cam.com/play/PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO.html

  • @AdroSlice
    @AdroSlice 4 ปีที่แล้ว

    Something makes me feel like this man has never worked with C# before. C# lambdas are super nice.