Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
nielsm
Jun 1, 2009



Entity Framework in .NET also uses magical names. Just recently I was trying to set up things with foreign keys, and it took me way too long to find the documentation explaining how to name all your properties in the entity classes so it can relate the properties and objects. I still haven't figured out how you're supposed to use the annotations when your database design/access pattern doesn't fit into the world view EF has.

Adbot
ADBOT LOVES YOU

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...
code:

string greeting = "Hello", name;
cin >> name;
cout <<  greeting << "World, " << name << "!";

For beginners, this is basically "uh I Guess I read and write by doing these weird arrows? Not sure why but ok?" And then you learn about manipulators you can use for clever programming tricks, but with No Idea what's even happening there. "Why does the magic word endl work, but also you're calling some kind of function called setw? I thought functions returned values, but I guess I can tell it to use a function or something?" And then you find out that cout and cin are the only places these <<s and >>s seem to work, except for maybe a couple other places where they mean Something Else?

Straight up inscrutable, I have absolutely no idea why someone decided "let's give new developers this magical footgun so they learn JUST ENOUGH to do stupid tricks, but don't know what is actually happening." They're powerful tools but they shouldn't be beginner tools.

RPATDO_LAMD
Mar 22, 2013

🐘🪠🍆
Cmake is designed to find dependencies by "magic" through functions like find_package, which basically end up looking for a hand rolled script for each package or else searching in a few standard folders like /usr/lib etc
Getting that poo poo to work on windows which has no standard folder structure anywhere for libs, includes, etc is a pain in the rear end, and because of all the "magic" involved specifying the folder locations explicitly ends up being more verbose and much less obvious than it would've been with a dumb non-magical build system like a plain makefile. And yes the documentation is really poo poo for this with a lot of super general detail on how the various functions work and no simple hello-world examples for "holy poo poo I just wanna link to a library it's RIGHT THERE please just show me three lines of code/config that will do this"

it seems like a pretty good system as long as you (a) never touch windows and (b) never have to actually write the drat cmake files yourself

redleader
Aug 18, 2005

Engage according to operational parameters

nielsm posted:

Entity Framework in .NET also uses magical names. Just recently I was trying to set up things with foreign keys, and it took me way too long to find the documentation explaining how to name all your properties in the entity classes so it can relate the properties and objects. I still haven't figured out how you're supposed to use the annotations when your database design/access pattern doesn't fit into the world view EF has.

you can override a bunch of stuff in OnModelConfiguring if you need to. ef's model really just has to be close enough to work, but it really really wants to be in control of your schema

Beef
Jul 26, 2004

RPATDO_LAMD posted:

Cmake is designed to find dependencies by "magic" through functions like find_package, which basically end up looking for a hand rolled script for each package or else searching in a few standard folders like /usr/lib etc
Getting that poo poo to work on windows which has no standard folder structure anywhere for libs, includes, etc is a pain in the rear end, and because of all the "magic" involved specifying the folder locations explicitly ends up being more verbose and much less obvious than it would've been with a dumb non-magical build system like a plain makefile. And yes the documentation is really poo poo for this with a lot of super general detail on how the various functions work and no simple hello-world examples for "holy poo poo I just wanna link to a library it's RIGHT THERE please just show me three lines of code/config that will do this"

it seems like a pretty good system as long as you (a) never touch windows and (b) never have to actually write the drat cmake files yourself

Cross platform support is the whole reason of being for cmake. I never touch the stuff on linux-only projects.
A lot of cmake's weirdness and complexity is exactly because it has to support windows.

QuarkJets
Sep 8, 2008

RPATDO_LAMD posted:

Cmake is designed to find dependencies by "magic" through functions like find_package, which basically end up looking for a hand rolled script for each package or else searching in a few standard folders like /usr/lib etc
Getting that poo poo to work on windows which has no standard folder structure anywhere for libs, includes, etc is a pain in the rear end, and because of all the "magic" involved specifying the folder locations explicitly ends up being more verbose and much less obvious than it would've been with a dumb non-magical build system like a plain makefile. And yes the documentation is really poo poo for this with a lot of super general detail on how the various functions work and no simple hello-world examples for "holy poo poo I just wanna link to a library it's RIGHT THERE please just show me three lines of code/config that will do this"

it seems like a pretty good system as long as you (a) never touch windows and (b) never have to actually write the drat cmake files yourself

If you're compiling on Windows then you're probably using Visual Studio or VS Code, which both have CMake extensions that you're supposed to be using to tell cmake where your dependencies live. Then stuff like find_package just works, because you've given it a standard folder structure for things like libs, includes, etc. that us linux developers get to take for granted (although not always, which is why cmake also supports non-standard paths)

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

darthbob88 posted:

The thesis of the presentation at time of posting is mostly this koan, plus leaky abstractions and possibly this XKCD. "Magic abstractions are very useful, but they will trip you up if you don't know about them and a little about what they do under the hood".

It's early and I will probably think of more interesting examples when I've had some coffee, but how about : string concatenation in languages like C# where strings are immutable? The language seems to promise that you can just go around using += to build your big string. But once you understand a little about what's happening under the hood, you know enough to realise when you actually shouldn't do that.

Athas
Aug 6, 2007

fuck that joker

darthbob88 posted:

And most relevantly here, the fact that 0.1 + 0.2 != 0.3 is the sort of breaking abstraction I can use in my presentation.

I don't think floating point is particularly magic. The cause-and-effect chain is perfectly clear, and most floating point problems are due to bad education. More generally, just because something is unintuitive or complicated, I don't believe it qualifies as magic.

In programming, "magic" to me is when cause and effect is decoupled. The magic Spring method names are a good example. A similar (but more benign) example is the Unix design pattern of having directories full of shell scripts that are automatically run by some hidden agent at various times.

One piece of magic that often bites me is when hidden files in the local directory have an effect on the commands I run, especially when those hidden files are silently created by the tool the first time it is run, and then freezes some bit of configuration or state that eventually becomes stale. I had some such problems with GHC environment files.

Xarn
Jun 26, 2015
Magic comments are literally lovely magic. Go has never found lovely idea it didn't like, so of course it is full of them.

Go code:
//go:build exclude
This tells go to exclude this file from build. I sure hope you won't typo something, like say, add a space somewhere it should go.

Same for magic function names.

Go code:
func TestThisIsATest(t *testing.T) {}
func TestthisIsNotATest(t *testing.T) {}
func TesTthisIsNotATestEitherAHAHAHAHAHAHAFUCKYOU(t *testing.T) {}

SupSuper
Apr 8, 2009

At the Heart of the city is an Alien horror, so vile and so powerful that not even death can claim it.

Beef posted:

Cross platform support is the whole reason of being for cmake. I never touch the stuff on linux-only projects.
A lot of cmake's weirdness and complexity is exactly because it has to support windows.
Actually a lot of Cmake projects are Linux-only. It deals with a lot of other quirks that still exist just in Linux land, like different compilers, package systems, feature detection, etc.

QuarkJets posted:

If you're compiling on Windows then you're probably using Visual Studio or VS Code, which both have CMake extensions that you're supposed to be using to tell cmake where your dependencies live. Then stuff like find_package just works, because you've given it a standard folder structure for things like libs, includes, etc. that us linux developers get to take for granted (although not always, which is why cmake also supports non-standard paths)
Here's a simple example where the abstraction leaks. Say I'm trying to use Boost.Date_Time in my project, so I just do something like:
code:
find_package(BoostDateTime)
Well that doesn't work. Boost.Date_Time is part of Boost, so you actually have to do:
code:
find_package(Boost)
Right? Well, Boost.Date_Time is an optional part of Boost. So you may have to do:
code:
find_package(Boost COMPONENTS date_time)
How do I know which is the right one? You don't, it depends on the environment, so you just have to if() around it and hope for the best. Already the magical-works-everywhere command has leaked and I have to work around it.

You can argue "oh well one of those environments is doing it wrong and they should fix it". Well that's the problem, find_package is trying to apply standards where there are none. What find_package inputs and outputs varies wildly based on the environment, libraries, package managers, versions, etc. You might as well just have a separate command for each use-case.

This is not entirely CMake's fault, because there's no such thing as "just drop in a dependency" in C/C++. But trying to paper over it with a magic command that keeps changing every version, just makes it more confusing when it doesn't work and you have to dig through CMakeLists to find out what's really happening.

Xarn
Jun 26, 2015
I have no idea how that is supposed to be an abstraction leakage. You want Boost.DateTime, you ask for DateTime subpart of Boost project.

Xarn
Jun 26, 2015
Want to talk about CMake abstractions leaking? Try doing anything with nested lists.

zokie
Feb 13, 2006

Out of many, Sweden
Since Java is so verbose and tedious compared to newer languages it seems to have created a culture of relying on a lot of magic stuff to avoid as much hiring boiler plate as possible. To me coming from frontend and C# starting to contribute to our Spring based backend felt like a lot of magic incantations.

There is a similar problem with C#, certainly when it comes to EF and asp.net, but it doesn’t feel like it’s being taken as far as Lombok for example.

Phobeste
Apr 9, 2006

never, like, count out Touchdown Tom, man
Yeah lol like find_project is complicated but it’s not magic. It does what it says on the tin: find stuff. That’s a complicated task so it has complicated behavior.

Phobeste
Apr 9, 2006

never, like, count out Touchdown Tom, man
A good example imo is coroutine transformations in languages like python and javascript where you add a magic tag and the function's behavior completely changes.

Async is ok-ish because there's a signature-level tag. In python,
Python code:
def hello():
    print("hello")

async def async_hello():
    print('async_hello')

hello()  # "hello"
async_hello() # does not print
asyncio.run(async_hello()) # "async hello"
JavaScript code:
function hello() {
    console.log("hello")
}

async function async_hello() {
   console.log("async hello")
}
hello() # "hello"
async_hello() # does not print, or, well, does but not exactly when you hit enter
await async_hello() # "async hello", at least in ecmascript evaluators
Having taught this to junior (and sometimes not so junior) devs, it makes sense once you know what's happening - particularly in javascript, you can say "this is exactly equivalent to return new Promise((resolve) => { console.log("async_hello"); resolve() })" - but it is extremely confusing that a function that looks like 99% the same as any other function just doesn't do anything when you call it.

The awful brother in python of course is the generator coroutine.

Python code:
def hello():
     print("hello")

def generator_hello():
    print("generator hello")
    yield

hello()  # "hello"
generator_hello() # doesn't print
next(generator_hello()) # "generator -hello"
The presence of a yield statement anywhere in a function's body turns it into a generator without any kind of tag on the function. The code inside the function body does not execute, any of it, even the stuff before the first yield, until you iterate the generator.

Type annotations don't even necessarily save you, because "a function that is a generator" and "a function that builds a generator from elsewhere and returns it" are typed identically:
Python code:
from typing import Generator

def i_am_a_generator() -> Generator[None, None, None]:
    print('i am a generator')
    yield

def i_return_a_generator() -> Generator[None, None, None]:
   print("i return a generator")
   return i_am_a_generator()

i_am_a_generator()  # does not print
next(i_am_a_generator()) # 'i am a generator'
i_return_a_generator() # 'i return a generator'
next(i_return_a_generator()) # 'i return a generator' \n 'i am a generator'
Again, this makes sense and is the only way it could work once you understand what's going on; but if you haven't seen it before...

fake e: it's not that it's leaking an abstraction; it's that the abstraction is presented non-obviously, especially in the case of generators since there's no signature-level tag. I think if the psf was approving a generator pep now, they would have had a top level tag; the async def tag was the first new token added to python in like a decade or something, they pushed back hard against it before (and the original async coroutine implementation was done with annotations that wrapped generators haha)

Phobeste fucked around with this message at 13:15 on Apr 29, 2024

SupSuper
Apr 8, 2009

At the Heart of the city is an Alien horror, so vile and so powerful that not even death can claim it.

Xarn posted:

I have no idea how that is supposed to be an abstraction leakage. You want Boost.DateTime, you ask for DateTime subpart of Boost project.
That doesn't always work. If DateTime was built as part of Boost (which was the default on Linux at the time), then find_package(Boost COMPONENTS date_time) will error. How are you supposed to know that without understanding what find_package is doing behind the scenes? And how are you supposed to handle both cases without working around it with ugly platform checks?
It's unreliable. If you look at most CMakeLists, they will either have platform-specific dependencies, or stick to simpler (to understand) abstractions like find_library or manually specifying paths.

necrotic
Aug 2, 2005
I owe my brother big time for this!
The javascript one is not quite accurate: any sync code is executed immediately when an async method is called, regardless of await. It does go off into the event loop until something actually async occurs.

JavaScript code:
async function async_hello(n) {
   console.log(`async hello ${n}`)
   await new Promise(resolve => setTimeout(resolve, n));
   console.log(`async goodbye ${n}`);
}

async_hello(200);
await async_hello(100);

quote:

async hello 200
async hello 100
async goodbye 100
async goodbye 200

This tends to surprise people, but that's exactly what happens in the Promise style:

JavaScript code:
function async_hello(n) {
   return new Promise(resolve => {
     console.log(`async hello ${n}`)
     return new Promise(timerResolve => {
       setTimeout(() => {
         timerResolve();
         console.log(`async goodbye ${n}`);
       }, n);
     });
   });
}

async_hello(200);
await async_hello(100);
The code only yields to the event loop when something yields to the event loop.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

zokie posted:

Since Java is so verbose and tedious compared to newer languages it seems to have created a culture of relying on a lot of magic stuff to avoid as much hiring boiler plate as possible. To me coming from frontend and C# starting to contribute to our Spring based backend felt like a lot of magic incantations.

There is a similar problem with C#, certainly when it comes to EF and asp.net, but it doesn’t feel like it’s being taken as far as Lombok for example.

You're absolutely correct that it's a pain in the rear end, and you have to work around it. I was going to suggest Google Guice, but I don't know how widely that's actually used.

Dagger is a nice DI framework for Java / Kotlin that performs compile-time injection, which may well be much nicer depending on whether you actually need to be able to add/remove modules at runtime

Clanpot Shake
Aug 10, 2006
shake shake!

Xarn posted:

Magic comments are literally lovely magic. Go has never found lovely idea it didn't like, so of course it is full of them.

Go code:
//go:build exclude
This tells go to exclude this file from build. I sure hope you won't typo something, like say, add a space somewhere it should go.

Same for magic function names.

Go code:
func TestThisIsATest(t *testing.T) {}
func TestthisIsNotATest(t *testing.T) {}
func TesTthisIsNotATestEitherAHAHAHAHAHAHAFUCKYOU(t *testing.T) {}

My favorite golang lovely idea is the magic date you use for date formatting. They could have used an unambiguous date like December 31st, but because it was created by techbros who can't see past their own nose you have to remember whether it's January 2nd or February 1st.

Jen heir rick
Aug 4, 2004
when a woman says something's not funny, you better not laugh your ass off
What about COM. Anyone remember COM? Or DCOM, or COM+. It was supposed to be this magical technology where you could register a COM object and then any program would use it. And when you update with a new version it would automatically work. And you could move those COM objects to a remote server and your program would just magically keep working the same. And any update on the server would be automatically be picked up by the local program.

Except that poo poo didn't work. It was a pain in the rear end. You'd update version and the program keeps picking up the wrong one. And you'd pull your hair out trying to find the right incantation to make it work. Any type of remote COM had to be carefully designed to be called remote. So that's a leaky abstraction. poo poo was worse than what we had, which was just shared libraries resolve by PATH. Which is what we have now, which shows you how lovely com was.

Of course I haven't worked with COM in like 20 years So I may not remember the specifics, but I do remember COM sucking poo poo, and having to pierce the vale of magic was often needed to get poo poo to work correctly.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Jen heir rick posted:

What about COM. Anyone remember COM? Or DCOM, or COM+. It was supposed to be this magical technology where you could register a COM object and then any program would use it. And when you update with a new version it would automatically work. And you could move those COM objects to a remote server and your program would just magically keep working the same. And any update on the server would be automatically be picked up by the local program.

Except that poo poo didn't work. It was a pain in the rear end. You'd update version and the program keeps picking up the wrong one. And you'd pull your hair out trying to find the right incantation to make it work. Any type of remote COM had to be carefully designed to be called remote. So that's a leaky abstraction. poo poo was worse than what we had, which was just shared libraries resolve by PATH. Which is what we have now, which shows you how lovely com was.

Of course I haven't worked with COM in like 20 years So I may not remember the specifics, but I do remember COM sucking poo poo, and having to pierce the vale of magic was often needed to get poo poo to work correctly.

My favorite part of COM was working with BSTR. It's a special string type that was used for com interfaces, as many different programming languages could interop with COM so you couldn't use, say, a std::string.

This doesn't sound so bad, but in the early days of windows, ms did performance profiling and found out that string allocations involving BSTR were slow - so MS built a BSTR cache into Windows - e.g. https://devblogs.microsoft.com/oldnewthing/20150107-00/?p=43203.

One of the issues with this cache is that it covers up issues involving string allocation across COM boundaries. For instance, say you have the audacity to return a bstr_t (which is a smart pointer to a bstr) from a COM interface. What will happen is that your program will work fine for about ten minutes, and then randomly crash with a heap corruption issue - this is because the BSTR cache will cover up that the bstr_t deallocated the string when it went out of scope, but once the BSTR is evicted from the cache you are hosed.

In order to catch an issue like this, you can set OANOCACHE=1 - this turns off the BSTR cache in the oleaut32.dll, so your program will crash immediately if you return a bstr_t instead of after ten minutes. This is also why you need to set OANOCACHE=1 when using mallocspy, as otherwise it may report bstr leaks erroneously.

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today

Xarn posted:

Want to talk about CMake abstractions leaking? Try doing anything with nested lists.

This is a great example. Lists in cmake are actually just space delimited strings. Good luck if you want to use strings that already have spaces in them, lol.

OddObserver
Apr 3, 2009

Ralith posted:

This is a great example. Lists in cmake are actually just space delimited strings. Good luck if you want to use strings that already have spaces in them, lol.

(Insert everything involving shell programming here)

darthbob88
Oct 13, 2011

YOSPOS

Athas posted:

I don't think floating point is particularly magic. The cause-and-effect chain is perfectly clear, and most floating point problems are due to bad education. More generally, just because something is unintuitive or complicated, I don't believe it qualifies as magic.
I'm using "magic" here both for things that are unintuitive in relatively absolute terms, but also for stuff that breaks intuition and the abstraction that's presented. "Floating point math isn't actually exact due to the limitations of IEEE 754" is less magic than "Go cares about the content of your comments" or "Spring JPA cares about your method names", but it's still magic the first time you encounter it.

quote:

In programming, "magic" to me is when cause and effect is decoupled. The magic Spring method names are a good example. A similar (but more benign) example is the Unix design pattern of having directories full of shell scripts that are automatically run by some hidden agent at various times.
For the purposes of this presentation, I'm using "magic" as in "very useful to, and poorly understood by, you the user". The first example I give for non-devs in the audience is Google Search. I could spend some hours reading and working out the full path of my search request between my browser up and down the OSI stack, through my local network to the wide internet, with a brief diversion into how the CPUs work on my computer and their server, and possibly researching how their algorithm determines what to show me. I don't, though, because I don't much care or need to know the details of how Google works. It is a magic black box to me. (And of course, as I also note in the presentation, I can only do that because other people do care about the details of how Google works, and spend a lot of time making it work better.)

Unfortunately, as much as I would like to just reduce the various development tools discussed in this thread to "magic black boxes", I need to understand, at least a little bit, how they work in order to do my job. I need to be aware that Go cares about my comments, that Spring cares about my method names, or that the build tool my project uses also reads a config file to determine what dependencies are available to each component.

lifg
Dec 4, 2000
<this tag left blank>
Muldoon
Copying richly formatted text from one application to another is magical, and often works, but sometimes your blog post will appear in a new font, and for a non-technical audience it’s hard to understand why it went wrong.

Clanpot Shake
Aug 10, 2006
shake shake!

Default parameters in Python might qualify? It's more "surprising" than "magical," but it trips people up.

Xarn
Jun 26, 2015

Ralith posted:

This is a great example. Lists in cmake are actually just space delimited strings. Good luck if you want to use strings that already have spaces in them, lol.

Dehumanize yourself and face the [==[[=[]=]]==]s

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

Xarn posted:

Dehumanize yourself and face the [==[[=[]=]]==]s

These loss edits have gotten very abstract

lifg
Dec 4, 2000
<this tag left blank>
Muldoon
Is Perl cheating? Because I just unblocked the memories of the ten+ years of experience I have with Perl.

There’s an implicit variable $_ , and some subroutines that return a value will implicitly set the $_ variable, if you decline to specify anything else to catch the return. Then there are some subroutines that take in a parameter, and if you omit it, they will implicitly take in $_ .

It’s all a lot of magic to let you write code that appears to not work even when it does.

biznatchio
Mar 31, 2001


Buglord
Perl is a devious creation by man, the pinnacle of our ingenuity and hubris, to create technical debt at a rate heretofore unseen and never to be outmatched.

necrotic
Aug 2, 2005
I owe my brother big time for this!
Is Perl one of the languages with a runtime configurable array base index?

darthbob88
Oct 13, 2011

YOSPOS

necrotic posted:

Is Perl one of the languages with a runtime configurable array base index?

Not anymore, apparently. Although there does appear to be a module for that.

biznatchio
Mar 31, 2001


Buglord

necrotic posted:

Is Perl one of the languages with a runtime configurable array base index?

Good news! There's a module in CPAN for that!

QuarkJets
Sep 8, 2008

Clanpot Shake posted:

Default parameters in Python might qualify? It's more "surprising" than "magical," but it trips people up.

Maybe, yeah. I don't know anyone who uses Python who understood the mutable default argument problem the first time that they encountered it, the behavior is not intuitive.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

necrotic posted:

a runtime configurable array base index?

:wtc:

What an incredibly cursed concept

Jeffrey of YOSPOS
Dec 22, 2005

GET LOSE, YOU CAN'T COMPARE WITH MY POWERS
One of my favorite manpage quotes is from perl:

man Switch posted:

If your source file is longer then 1 million characters and you have a switch statement that crosses the 1 million (or 2 million, etc.) character boundary you will get mysterious errors. The workaround is to use smaller source files.

Xarn
Jun 26, 2015
Oh it's characters and not lines. I have some (autogenerated) source files like that.

Adbot
ADBOT LOVES YOU

more falafel please
Feb 26, 2005

forums poster

A million characters is probably pushing it on account of average line length of source being pretty low (whitespace lines, brackets, etc), but I've definitely worked in (non-generated) files that were 50k+ lines, so I could see that biting you. It's funny that it's exactly 1,000,000 and not 2^20 though.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply