Who needs trigger warnings anyway? (Trigger Warning: trigger warnings)


  • Banned

    It doesn't compile.



  • So even though b was supposedly immutable, it now can't do what it did before?

    What's the benefit of always putting values beyond this layer of indirection? I mean, it's a pretty significant complexity leap to go from "this thing is the value" to "this thing refers to a value".


  • Banned

    @Kian said:

    So even though b was supposedly immutable, it now can't do what it did before?

    Mutability is of a value, not of a variable, even though it's defined by the variable, not the value. It's a tad complicated to comprehend if nitpicking details is how you start learning a new language, but it makes perfect sense and is very intuitive once you actually use it. Anyway, that snippet is a perfect example of move semantics. Since move doesn't change the value in any way, moving it around doesn't require mutability - but when you move a value out of variable, the variable becomes unusable until you assign a new value to it. It should be noted that non-mutable variables can only be assigned to once - so after you move out of it, it becomes useless.

    @Kian said:

    What's the benefit of always putting values beyond this layer of indirection?

    Less restricted move semantics, reduced need for mutability, more optimization opportunites for the compiler.

    @Kian said:

    I mean, it's a pretty significant complexity leap to go from "this thing is the value" to "this thing refers to a value".

    No one said Rust is easy.



  • @ixvedeusi said:

    What is your beef with Python's type system? For a non-statically typed language it seems to work quite well to me...

    The compiler doesn't stop you from doing anything. You can overwrite any value in an object at any time, usually accidentally if you're not evil. You can literally overwrite the type of the object, effectively changing the type at runtime. You can pass the wrong type to a function and you might not even notice it if the fields and operations are compatible. There are no breaks, no rails, no safeties, and instead of crashing you just plough through walls without so much as a warning. It's a nightmare.

    @dkf said:

    Some people don't understand the difference between putting types on values and putting types on variables.
    That's actually a pretty good way to explain it.

    @ixvedeusi said:

    What about objects which are completely optimized away by the compiler? Don't they have an identity?
    If a tree falls in the forest, and no one is there to hear it, does it make a sound? Point being, the compiler wouldn't optimize it away if you took its address! I'm OK with that.


  • Discourse touched me in a no-no place

    @LB_ said:

    You can literally overwrite the type of the object, effectively changing the type at runtime.

    Awesome! There are some really neat things you can do with modelling the world in that case. Static type systems need to use a much more complex delegation mechanism to achieve the same sort of effect, with far more code and a lot more complexity, almost all of which is just there to work around the lack of flexibility at the more fundamental levels.

    Don't get me wrong: there's much I don't like about Python. It rubs me up the wrong way, feeling clunky and fussy. But they've got some good stuff too. Does it let you create your own subclass of the class of classes so you can do really interesting stuff during object creation, or are we stuck with the silly old world of factories and/or template madness (i.e., what Java, C# and C++ do)?



  • It would be perfectly fine if it was more rigid, but changing the type in Python is like changing any other field - none of the other properties of the object are changed and so it still has all the old data from the old type and none of the new data for the new type. That's a big problem for me. If it was something more controlled and had better guarantees from the language, I wouldn't be calling it a :wtf:.

    In-place derivation is something I want to try and incorporate in my own language, but I don't know about the feasibility of it.



  • @LB_ said:

    The compiler doesn't stop you from doing anything. You can overwrite any value in an object at any time, usually accidentally if you're not evil. You can literally overwrite the type of the object, effectively changing the type at runtime. You can pass the wrong type to a function and you might not even notice it if the fields and operations are compatible. There are no breaks, no rails, no safeties, and instead of crashing you just plough through walls without so much as a warning

    This is in fact one of the things I like so much about Python: It doesn't get in my way, doesn't try to be "helpful" by preventing me from doing the stuff I need to do to solve my problem. I have complete access to the language internals. I can change the way class creation, object instantiation or attribute access works, or change the type of an object at runtime, if that helps me solve my problem. And the thing is, it actually works. The engine does not crash and burn or get confused because your object just changed class, it just chugs along with your trans-typed object, still providing consistent behavior. This makes it possible for example to create extremely flexible, convenient and easy to use interfaces.

    Of course you have to be a bit careful about what you're doing. That's why you have unit tests. I prefer spending a moment from time to time debugging a spellar than spending all my time fighting the compiler / runtime.

    It also depends a lot on the size of your project. Python works well for anything from single-file one-off scripts to small / medium projects of maybe a few thousand lines of code; I've never tried to do anything bigger and I've never really worked on a python project in a team.

    @LB_ said:

    changing the type in Python is like changing any other field

    That's not quite true. If anything you do in python involves __double_undescores__, you're messing with language-internal stuff and should be careful. You don't type those double underscores accidentally. So if you don't want to change an object's type, don't do it!


    But then, I like C++ (we have one of those intense hate/love relationships) so people tend to not take my tastes in programming languages seriously...


  • Discourse touched me in a no-no place

    @ixvedeusi said:

    trans-typed object

    OOOooooh! That makes C++ the language for the trans-type-phobic! :) 🍤 🏆



  • I guess it's just personal preference then. I want the compiler to make sure I do things right. More importantly, I want the compiler to make sure other people do things right. You can get pretty good coverage with unit tests, but some things are more easily found by a simple compiler error, or warning (as in the first post of this thread).



  • @dkf said:

    Does [Python] let you create your own subclass of the class of classes so you can do really interesting stuff during object creation, or are we stuck with the silly old world of factories and/or template madness (i.e., what Java, C# and C++ do)?

    If I understand correctly what you mean, what you ask is accomplished through _metaclass_.

    One useful application is the abc module, which lets you create abstract base classes that give a compile-time error when one of the methods that are decorated with @abstractmethod isn't implemented in the derived class.


  • Discourse touched me in a no-no place

    @OffByOne said:

    If I understand correctly what you mean, what you ask is accomplished through metaclass.

    It might be. I don't know the python ecosystem nearly well enough to tell for sure. (And yay for Discourse liking to highlight quoted python internals… ;))



  • Presented without comment:

    if @a.responds_to?(:abs)
      # here @a is not guaranteed to respond to `abs`
    end


  • In the "Everything is an object" section we said that an object has a
    type and responds to some methods, which is the only way to interact
    with objects, so we'll need a name and age methods. We will store this information in instance variables, which are always prefixed with an at (@) character.

    In other words, no public fields, only getter methods.



  • If you redefine a method, the last definition will take precedence.

    You can invoke the previously redefined method with previous_def:

    class Person
      def become_older
        @age += 1
      end
    end
    
    class Person
      def become_older
        previous_def
        @age += 2
      end
    end
    
    person = Person.new "John"
    person.become_older
    person.age #=> 3
    

    All the fun of subclassing, except it's just one class.



  • If you use a single uppercase letter as a type restriction, the identifier becomes a free variable:

    def foo(x : T)
      T
    end
    
    foo(1)       #=> Int32
    foo("hello") #=> String
    

    Yes, it's the same syntax as for regular types, but the identifier must follow an arbitrary rule.



  • Note that private methods are visible by subclasses

    Because fuck other languages and how they do it.

    A class inherits all instance variables and all instance and class methods of a superclass, including its constructors (new and initialize).

    The next paragraph:

    If a class defines a new or initialize then its superclass constructors are not inherited:

    Now it's contradicting itself.

    When a variable's type combines different types under the same class hierarchy, its type becomes a virtual type.

    The real reason the compiler does this is to be able to compile programs faster by not creating all kinds of different similar unions, also making the generated code smaller in size.

    They couldn't stand their own crazy system.

    An extend makes a type include methods defined in that module as class methods:

    Includes and/or namespaces, except when they are not.

    Macro defs allow you to define a method for a class hierarchy and have that method be evaluated at the end of the type-inference phase, as a macro, where type information is known, for each concrete subtype.

    Yes, now we are running code inside the compiler. But wait, it gets better.

    Special macros exist that are invoked in some situations, as hooks: inherited, included and method_missing.

    • inherited will be invoked at compile-time when a subclass is defined.
      @type becomes the inherited type.
    • included will be invoked at compile-time when a module is included. @type becomes the including
      type.
    • extended will be invoked at compile-time when a module is extended. @type becomes the extending type.
    • method_missing will be invoked at compile-time when a method is not
      found.

    And you thought that the C preprocessor wasn't fun enough.

    Constants can be declared at the top level or inside other types. They must start with a capital letter:

    Yup, anything that begins with an upper case letter is a constant. Obey our naming convention, or else!

    And then we get the yield system:

    Methods can accept a block of code that is executed with the yield keyword.

    When using blocks with yield, the blocks are always inlined: no closures, calls or function pointers are involved.

    It's such a mess that I can't do it justice.

    And finally, ever heard of build systems?
    The design of the language inhibits build systems that attempt to do minimal rebuilds.

    Writing a program in a single file is OK for little snippets and small benchmark code. Big programs are better maintained and understood when split across different files.
    To make the compiler process other files you use require "...". It accepts a single argument, a string literal, and it can come in many flavors.

    The attitude shown here shows that they've never worked with a big project.



  • @henke37 said:

    Presented without comment:

    That's very WTF. Effectively, it can do run time introspection on local variables, but no on globals, class or instance variables. But doesn't error / warn if you do it wrong.

    Please, though, at least put a link in to the documentation, makes it easier to follow. The previous was here : http://crystal-lang.org/docs/syntax_and_semantics/if_varresponds_to.html

    It certainly appears to be a language designed by crack / pole smokers.



  • Why is there this unnecessary assignment over temporary variables??



  • @tufty said:

    put a link in to the documentation

    Thanks. I had lost track of what language @henke37 was posting about. (The previous couple of posts were about Python.)



  • I think the compiler is allowed to optimize those out. But I bet it doesn't.


  • Discourse touched me in a no-no place

    @HardwareGeek said:

    Thanks. I had lost track of what language @henke37 was posting about.

    It smells a lot like Ruby, but less well-baked and even crazier. My opinions on Ruby verge on the outright unprintable to start out with, so I guess I'll stay away from Crystal too.



  • That still doesn't explain why a developer would ever go that route. I
    don't really understand the example given by Crystal, as there are these 2
    lines with temporary variables.



  • @Snowman25 said:

    That still doesn't explain why a developer would ever go that route.

    I thought that the Python documentation explained this concept in the same way? It's just an example showing a breakdown of what the observed effects are.



  • None of those sound too bad to me. Obviously a bit weird coming from a c.*/Java perspective, but the idea of translating Ruby's expressiveness into a statically checked language impresses me to no end. Crystal might be my favorite new language until the next one that comes out.

    Honestly, I just want to hear @scholrlea's opinion on this language. Would you say it's an improvement over ruby? How does it stack up against lisp?



  • @tufty said:

    Effectively, it can do run time introspection on local variables, but no on globals, class or instance variables. But doesn't error / warn if you

    That's not how I read it.

    a = some_condition ? 1 : "hello"
    # a :: Int32 | String
    
    if a.responds_to?(:abs)
      # here a will be Int32, since Int32#abs exists but String#abs doesn't
    else
      # here a will be String
    end
    

    Seems to be saying that it reduces the static type of the variable inside the arms of the if. And obviously it's not gonna work for non-locals because they can be changed by other methods called within that same if branch.



  • I am SpartacusBlakeyrat!

    (Come on, everybody can join in the fun!)



  • @ScholRLEA said:

    I am Spartacus@blakeyrat!

    Filed under: You were doing it wrong


  • Java Dev

    Will the real slim shady please stand up.





  • @ScholRLEA said:

    I am SpartacusBlakeyrat!

    Je suis Blakie.


Log in to reply