Demo: Renamers, Static If

แชร์
ฝัง
  • เผยแพร่เมื่อ 26 ส.ค. 2024

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

  • @cacheman
    @cacheman 7 ปีที่แล้ว +11

    26:05 I've adopted compiling with '-fvisibility=hidden', I'd probably be happy with #scope_file as the default. Something about having to explicitly 'litter' every external symbol speaks to me.
    That said, having a default symbol scope that's just for your current 'project' probably makes more sense.
    1:05:00 I will unashamedly expose myself as a member of the anti-globals brigade. I don't think there's anything "weird" about it, I've just been bitten too many times by someone thinking something is a global when it really shouldn't be. They interfere with threading, and tend to complicate (proper) initialization and shutdown of libraries. "Oh, why would a library need to be shutdown and cleaned up, just exit!" great, I'm trying automatically leak test my code, now I have to write valgrind exceptions because you thought a pointer-indirection would slow down your HTTP library. Thanks". I grew up programming on the C64, so this isn't some "university OOP" thing. I appreciate what you're saying here, but I'll remain unconvinced about the greatness of globals in library code. Shit happens, needs to be dealt with, sure.
    1:11:40 So isn't it tempting to make the 'renamer' return a new function instead of only a string, i.e allow you to decorate your imports? I'm sure you could do crazy stuff with something like that (please don't take this as encouragement). Somthing for a post-closures demo perhaps.
    Anyway, I appreciate you and your team's work on this language. I'm really looking forward to trying it out for myself one day, having been disappointed in some other recent languages.

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

    I came to the comments expecting people ranting about submodules and namespaces, I was not disappointed
    It's just so comfy when you've been using it for decades

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

    Aw hell yeah, I've been waiting for another demo :D

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

      fuck yeah porra, me too

  • @omgomgomgd
    @omgomgomgd 7 ปีที่แล้ว +12

    Oh boy god, that separate instances of global stuff at 1:07:13 - I am in awe, this would be so useful.

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

      To me it seems like the equivalent of creating multiple instances of a class but creating a special case for this and building it into the compiler. Or am I missing something?

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

      @@CsharpPreza kind of, but you would still be able to link statically to that "instance".
      The types defined in each "Instance" are actually different types, and the compiler type checks them as such.
      He also lays out the idea perfectly at 1:10:40 ish.
      To reiterate that: it's all of the extra crap you have to do to write things '''''correctly''''' ""just in case"" you want to reuse something- you don't have to with this feature. The language will let you reuse global things as if they were separate, so you can just write some modules globally, and instantiate extra ones if needed.

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

    lots of underscores in a numeric constant could be useful for documenting bitfields if the previous or following line was a comment defining what each bit field is used for. I like this idea!

  • @blenderpanzi
    @blenderpanzi 7 ปีที่แล้ว +13

    Well, I like how it is in Python:
    import lib1
    lib1.func()
    from lib1 import func
    func()
    from lib1 import *
    func()
    import lib1 as lib2
    lib2.func()
    from lib1 import func as func2
    func2()
    You can do the same with slightly different syntax in ECMAScript 6.

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

      Gotta agree here. One of the best things about Python is that the module-level namespacing and explicit module imports make the code eminently readable to humans: an IDE (or grep) is still handy, but it isn't required to figure out what's going on. Every name used in a file is either declared in that file or directly imported into that file, and, as long as you don't use `import *`, it's easy to connect names to their declarations.

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

      Yes, connecting names to their declarations is something I learned to appreciate, especially when programming in Ruby, where you can't do that, at all. In Jai the renaming function would need to be executed by an IDE to support auto-complete. I really don't like that.

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

      Hmm, I guess the renaming function would make refactoring also very hard.

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

      > the renaming function would need to be executed by an IDE to support auto-complete
      That is not true at all.

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

      It's not? But then how does the IDE know where to get the list of names to present you when you type `Nontendo_`? Where is my error?

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

    As someone interested in operating system development, I'd like to use the compile time execution to pre-build tables like the GDT and IDT, but for that to be possible (specifically for the IDT) I'd need to be able to give it pointers for the CPU to access. Specifically the IDT contains function pointers that tells the CPU where to jump to when an interrupt is received. Will I be able to include pointers to where code will be in compile time generated structures? One way to do it without using your own linking is to specify the pointer as a relocation that's filled in at link time, though that might only work in some cases.
    Worst case scenario, I build most of the structure at compile time and add in the pointers at run time.

  • @Xavier-cd6fx
    @Xavier-cd6fx 6 ปีที่แล้ว

    It is really interesting the way that you manage the importation of libraries, it solve some issues I already have and I don't think that there is an other language that take care of that.
    I remember that firsts versions of the pvrtc library (texture compresser for mobiles) was using globals that didn't allow us to compress textures with concurrency, the workaround we found was to create a binary around the library.

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

    This is with regard to the default external visibility of a file. When working in a big team, usually you want the default behaviour for new a newly added member to be something like file-scope only.
    Say you add a load of methods and expect them to be usable from some other context, but in this scenario the default is file-scope only... When it comes to compiling there'll be a "method not found" error, so you just go and add externalize those methods. No big deal.
    However, if the default behaviour is to be visible externally then a worse scenario can happen. Say you add these methods with the notion that they're only supposed to be used within this file's scope, but forget to add the "use in this file only" attributes (easily done) then at some later date another developer, not necessarily junior, could start making use of these supposedly private members and then all hell breaks loose... and probably after the code has shipped!
    At the very least there should be an overall compiler option to choose that default behaviour.

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

      I agree that this is the sort of default where a compiler option/directive is called for, but can we please not go down the "make the language safe for novice programmers" route? Thanks. That's what makes me loathe all these other new languages.

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

    I'm pretty sure you could buy a Nontendo GameBlock in China or the Middle East if you looked hard enough!

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

    I guess renaming happens on the already constructed symbol table for a given file, and that's how you can determine whether you've got two aliases for the same thing or not.
    If you want to avoid globals, I think lexical closures are the way to do it. As far as I can tell, they do hide state from stuff that shouldn't have access to that state while all the overhead disappears at compilation time, since lexical scope does not need any kind of runtime overhead.

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

    +Jonathan Blow the bit rot will happen in the included file most of the time though, not in the #load statement ... and, as far as i understood the mechanics, only lexical analysis takes place (as in is it a valid token), without any further analysis (no semantic analysis by the parser). so, i guess, some colleagues will still call you 'cause you broke something ;)

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

      It is true that this will not eliminate all problems, but who ever thought that was possible?

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

    can't wait to mess around with it!

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

    can the underscores in a number be at any digit place?
    would 10_00, 1_000 and 100_0 be the same?
    Never mind, I see you got to it just a little later in the demo.

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

    I suggest that compiler should not allow multiple underscores in constants. If somebody accidentally puts 2 underscores, it might cause problems when searching through files down the road.

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

    js modules seems good:
    import seeded_rand from './lib/seeded_rand'
    import {fromJson, toJson, stdDeviation as stddev } from './lib/functions'

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

    "Nontendo Gameblock"
    nyehehehe

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

    I was REALLY needing this yesterday when I was writing a parameterized header checker and couldn't... Yeah. C++ can't really do static ifs... enable_if kinda works but it's a total hack and it's ugly as sin.
    C++ is also terribly deficient at compile-time strings in templates.

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

      c++17 has `if constexpr` if you want to use that.

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

    I'm not too sure about the renamers.. feels like it could easily become a mess if things are called different things in different parts of the code base (and also hard for IDEs to understand for easy code navigation).
    I think it'd be more useful to import into a namespace like #load "whiz" as wl; and then use wl.Slider. and in cases where you want it in your own name space you can use 'using' as normal.
    The renaming is very powerful, but maybe too powerful. Wrappers (that would get inlined) for specific cases where you really want a rename is probably much cleaner to deal with.

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

    But. But. With all this renaming, how are we going to copy-paste stuff from SO?

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

      Just as easily as ever? Maybe easier, since you can just use renaming to alias anything the SO answer named differently :)

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

    I feel like Node.js solves this whole problem really well.

    • @0xCAFEF00D
      @0xCAFEF00D 7 ปีที่แล้ว

      Programming in general?

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

      no, the load system

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

      but I guess also that.. lol

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

      Well, I prefer the ECMAScript 6 way of importing modules (which basically is just syntactical sugar around the require() function, but still).

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

    is there a way to get to a specific #unshared instance somewhere else besides the immediately following scope?

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

    1:24:18 Can you do something like
    #bake_me(OS_WINDOWS);
    to use the compile-time constant, or would it just be
    #if OS_WINDOWS {
    bake_me(true);
    } else {
    bake_me(false);
    }
    or
    #if OS_WINDOWS {
    is_windows: bool = true;
    } else {
    is_windows: bool = false;
    }
    bake_me(is_windows);
    I could see that the function call alone might cause an error at compile time on non-Windows systems since OS_WINDOWS will never be defined and could be of ambiguous type.

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

      Compile-time constants are understood as constants, yes, so you could just pass OS_WINDOWS directly.

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

    You may touch on it later, but i feel like i have to ask while the question is still fresh in my mind.
    The "dynamic" prefixing on functions from libraries and modules seem like duplicated functionality. I remember in an old demo you implemented the "Using" keyword, and it was my understanding that it was supposed to be used for when you have a namespace that you want to include locally.
    Is there something that this new way of doing namespacing does better? Will it kill of the old "using namespace" syntax? What does this solution offer that something like namepaces in C# or Java doesn't?
    I'm afraid dynamic renaming of function names will make it difficult to understand the code, and could possibly create high friction between multiple collaborators.

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

      I could be wrong, but the unshared functionality seems very analogous to a singleton. Another way of doing the same in C++/C#/Java would be a with a global object, which would also solve the namespacing problem, and makes the entire thing runtime allocatable. I'd also argue that in this specific instance, the OOP method is more composable and easier to reason about.
      I think you are getting a little close to being better of with standard OOP in this specific case.

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

      Probably what will happen eventually is that 'using' can take a renamer also. It's not really duplicated functionality, so much as that things like #load / #import automatically have a 'using' attached to them to pull stuff into the namespace. If you tried to get rid of that in the name of orthogonality you'd probably just make things more of a pain.

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

    I guess this renamed prefix will mix bad with ide tab completion.

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

      I don't think it complicates it too much. The IDE will have to figure out which identifiers are imported by a #load statement anyway, so it can just execute the compile-time procedure when doing that and add the renamed identifiers to its index instead.

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

    When #loading a file will one be able to change the default values and "input names" of a procedure?

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

      That seems like something that you would do in a compile-time metaprogram (but we don't supply a way to do it yet unless you want to re-generate new program text and mix that into the program).

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

    Have you considered making global state thread local like D lang does?
    tour.dlang.org/tour/en/multithreading/thread-local-storage

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

      Well you want the ability to have non-thread-local global state, so the question is really just what the default is, which I don't have much of an opinion on right now, but I would guess the right default is non-thread-local, because TLS has a performance impact.

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

    interesting ideas, but
    IMO global data *IS* bad for multi-threading. I definitely support Rust's choice to declare globals 'unsafe'* . The side of OOP based on passing system pointers around is actually helpful IMO: it communicates side-effects, helping you reason: especially with 'const correctness' (immutability) i.e. making mutability or read-only dependance crystal clear. ("render reads the game state, but modifies the renderer.. update modifies the game state, but doesn't touch the renderer..." ... all formally encoded in function signatures). With overloading you can save expressing that information 'informally' in the function names.. say more through the type-system in a machine checkable way.
    The *problem* of 'class-based-OOP' was excessive coupling, but that was solved just fine with extension-methods/UFCS/traits etc. In Rust you can operate out traits and implement your ent.render() ent.update() in independent modules just fine.
    A big chunk of the 'namespacing' issue can be handled by 'overloading using the 1st parameter'. gl.flush() gui.create_window().
    something like the **Vulkan API** is basically a light form of OOP by necessity (it just happens to be expressed in C, passing object pointers around), e.g. independent **'command-buffer' objects** safe to be produced by multiple threads, assembled into a queue object managed by one thread... if those calls operated on global state it would be a mess.
    We have unfortunate split opinion in the C++ community preventing UFCS getting added. Most of the world knows this 'dot syntax' now (and great tools use it).. and has moved on with languages that fix the problems; unfortunately the C++ committee are dragging their heels, listening to the people who now associate the 'dot-syntax' with the coupling or vtable hazards (this is 'throwing the baby out with the bathwater'). the 'dot' is great for this, syntactically light and well known
    (* although overall I find rust too restrictive/and over-explicit in other ways, which is why I'm still interested to see whats being done here..
    I don't like the fact Rust requires the trait in the namespace, I think that is excessive.. "file::type::trait::function" I would ideally like completely general overloading & UFCS, with the traits/'type classes'/'interfaces' being optional )

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

      There will be some stuff later on to help deal with threading and restricting global access, but even without built-in features for that stuff, you can do it in a compile-time metaprogram that enforces your house style.

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

      i suppose you might make the call-graph introspectable, that could be interesting, (but if there are lambdas involved thats harder to predict). I gather rust used to have an 'effects system'.. maybe compile-time code using the callgraph could do something similar

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

    i am also a bit worried that the renamer can be used to create a huge amount of work if someone changes the renaming code. suddenly all your identifiers are busted and this is probably not a quick refactor.

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

      Who's someone? The language should be there to support YOU, not to try and keep programmers in check. There are a million bondage languages for that (see also: Rust).

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

      eloj i dunno. i work on a team with 200 other engineers so maybe one of them. i see how this is useful but without some codegen capacity this could be bad.
      can we agree that a language should not cause insurmountable refactoring problems?
      edit: i also think it might be confusing to have something named differently in a ton of different files. as code grows it will be a hassle to keep track or what is named where. the renamer is definitely a weapon to use sparingly.

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

      I'm honestly confused about what you're asking. If you don't trust the members of your team, then I don't know what to say other than I don't think that is a language problem, and that if 'someone' (who isn't you, clearly, it's always someone else) wants to merge code that is problematic, turn it down during code review.
      If anything, this seems like a feature that solves a problem (a rare one for me, but still) without adding much of any burden. Don't like it, don't use it.
      If it comes down to, "but someone else might..." then it's not a technical/code problem, that's a management problem. I can't speak for Jonathan, but I don't want my programming language to try and solve those kinds of problems. This leads to what I call 'bondage languages', where everything is a pain because the language designers decided that there's One True Path, and by golly, you're all walking it.
      See also Golang's insistance that an unused variable is always a fatal error.

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

      ok so imagine you made an error in your naming algorithm, now all your identifiers are busted. its not anyone else's fault. The fact that you can ruin all your identifiers seems super risky and it will cause me to use this feature less enthusiastically because of this risk. That is my point. I'm not saying we shouldn't have it. I am saying we should be aware of this risk and that using it can make your code brittle.

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

      I agree with Kyle here. It takes quite a smart editor to know what the effect will be of an arbitrary string transformation in the rename function and to adjust the affected identifiers appropriately. "import x as y" is less brittle in that regard but also less powerful of course. Then again, maybe the *compiler* can just directly tell the editor *exactly* which identifiers were output from the rename operation, then the editor would have no problem adjusting for the rename change. Additionally: Jon actually mentioned that you'd be able to just load a file into a namespace, which is also less brittle (e.g. wl.CreateSlider instead of wlCreateSlider). My thought is that if you don't need the power of an arbitrary string-based transformation, then use one of the simpler (future) options available.

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

    Wait? What? 0h? What's wrong with 0x? Did I miss a video discussing that aspect of literals?

    • @Shadow10011
      @Shadow10011 7 ปีที่แล้ว +4

      I believe that 0x is used for integer literals and 0h is used for floating point literals

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

      David Johnston 0x are for denary literals, 0h are hex literals

    • @Shadow10011
      @Shadow10011 7 ปีที่แล้ว +4

      Why would you ever write a = 0x10 instead of a = 10?
      It makes more sense to me that 0x would be integer hexadecimal literal and 0h would be floating point hexadecimal literal.

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

      Ahh. Thank you.

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

      I may have base format PTSD. There are those of us who deal with 8'h10 (C), 0x10 (Verilog) and 10h (assembler) daily.

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

    The renamers are actually really ugly. A module system or a package system, where you reference names by the module/package, where they are located (such as module::function(), or package.function()) is much better.

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

      I said in the talk that this would be coming also. Multiple times.

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

      But what's the point of renamers, if you have packages? I don't like it when there's too many ways to do one thing, 'cause then you have to think about which one to use.
      You don't want to make a language, where "best practices" say "don't use this feature".

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

      Like, I don't mean to be rude. I know you're figuring things out and exploring the solution space.
      Nonetheless, I think renamers are a particularly bad idea, terribly easy to get in trouble with them.

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

      You also don't want to make a language where nobody ever has to consider best practices because there is only one way to do anything, even if that way is incredibly awkward for some uses.
      Renaming allows clean adaptation of external libraries to fit with the style and structure of your project. The module.function() isn't better or worse as far as I see it, though it does obligate verbosity, which I vaguely recall being one of the thing Jon was trying to avoid.