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
tef
May 30, 2004

-> some l-system crap ->

comedyblissoption posted:

i like it and i think readable is a reasonable way to describe my preference as if it is objective

Adbot
ADBOT LOVES YOU

comedyblissoption
Mar 15, 2006

which of those approaches do you prefer? is there another approach?

keep in mind that the second case where you have a const but you assign to it along branching paths might defeat the entire purpose of making a variable const in the first place when the logic gets complicated enough. intent might also not be reasonably analyzed by a compiler.

comedyblissoption
Mar 15, 2006

generating an intermediate value to assign to a const is more complicated and more verbose.

creating an intermediate function is also more complicated and more verbose.

i haven't seen assigning to a const value along multiple branching paths and it might just be a terrible language design choice

gonadic io
Feb 16, 2011

>>=

MALE SHOEGAZE posted:

is that really true? don't languages like haskell just present you with an immutable interface for data but under the hood much of it is mutated anyhow because otherwise it would be super slow?

asking for amfriend

nobody is saying literally never use in-place mutation. it's just about how many hoops the compiler makes you jump through, which (the theory goes) make code that uses mutation more likely to be correct.

in some langs absolutely everything is mutable and you have no guarantees at all and it's poo poo

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
you don't have to declare the function, just call a closure. this is fine for the small number of times that you actually want to do a switch just to produce an intermediate value and it doesn't make more sense to make that a general function for the type

Soricidus
Oct 21, 2010
freedom-hating statist shill

comedyblissoption posted:

in list, an expression invoking a function is written with the following syntax:
code:
(function arg1 arg2 arg3)
to sequentially chain expressions together, you can use progn which is a special form (not really a function) which takes multiple expressions and sequentially chains them together:

code:
(progn
    (foo1 arg)
    (foo2 arg)
    (foo3 arg))
rust semicolons are a similar construct for chaining expressions together

if it was just
code:
rust: foo; bar; baz
lisp: (progn foo bar baz)
then nobody would be complaining. that is fine and logical.

what people are complaining about is
code:
rust: foo; bar; baz;
lisp: ????
(there is no lisp equivalent because lisp doesn't confusingly create an implicit void value if you mistakenly use punctuation the way it's used in every single other braces language)

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

MALE SHOEGAZE posted:

is that really true? don't languages like haskell just present you with an immutable interface for data but under the hood much of it is mutated anyhow because otherwise it would be super slow?

asking for amfriend

this is a really weird question

under the hood, every programming language mutates memory. this is because computers do not come with the end-state of our programs burnt into rom

i am positive that there are haskell optimizations that find ways to modify things in-place when they recognize that that's possible, or better yet avoid creating the original data structure when it's immediately discarded. if you're imagining that that applies super-commonly outside of very local things like chaining maps and filters, you are probably mistaken

comedyblissoption
Mar 15, 2006

rjmccall posted:

you don't have to declare the function, just call a closure. this is fine for the small number of times that you actually want to do a switch just to produce an intermediate value and it doesn't make more sense to make that a general function for the type
can you give me an example of what this would look like

this is what i think of when you say "call a closure"
code:
const int x = fn () -> int {
    if (some_bool) {
        return 42;
    } else {
        return 420;
    }
}();
also i find that when coding with an immutable/const style, assigning based on an if/else or switch/match happens very frequently and not rarely. Wrapping stuff in functions is a lot more verbose and complicated compare to using a match or if/else that returns an expression.

comedyblissoption
Mar 15, 2006

Soricidus posted:

(there is no lisp equivalent because lisp doesn't confusingly create an implicit void value if you mistakenly use punctuation the way it's used in every single other braces language)
that makes sense

i suspect they struggled with wanting lambdas to look like:
code:
|x| x + 1
instead of
code:
|x| x + 1;
and trying to base a consistent syntax around that instead of having special edge cases

maybe there's another reason

Bloody
Mar 3, 2013

if exprs are literally just different syntax for ?:

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?
i think comedyblissoption makes a good case wrt "everything is an expression" and ease of working with immutable values. i suppose you could make the case that the ternary operator helps but why have what's basically two different kinds of if statements?

JawnV6
Jul 4, 2004

So hot ...
rather than continue debating the one thing perl got right, apparently, can one of the rust experts explain this bit

quote:

I would argue that the only real difference at the runtime level between programs you can write in LLVM and programs you can write in Rust is that Rust requires all of your programs to be safe, i.e. not have memory errors/data races
goes on to say it does this without imposing any runtime constraints, couple paragraphs after claiming LLVM doesn't have loops

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

comedyblissoption posted:

can you give me an example of what this would look like

this is what i think of when you say "call a closure"
code:
const int x = fn () -> int {
    if (some_bool) {
        return 42;
    } else {
        return 420;
    }
}();
also i find that when coding with an immutable/const style, assigning based on an if/else or switch/match happens very frequently and not rarely. Wrapping stuff in functions is a lot more verbose and complicated compare to using a match or if/else that returns an expression.

assume for the sake of argument that the statement-based language we're talking about does have a ternary expression. this is a good bet because i can't think of any off-hand that doesn't. so this is literally just about switch

yes, that is the general idea of the syntax. in swift it would be:

Swift code:
let x = {
  switch foo {
    case .a: return x
    case .b: return y
  }
}()
if swift were expression-based it would be:

Swift code:
let x = switch foo {
    case .a: x
    case .b: y
  }
the verbosity difference here is not huge

comedyblissoption
Mar 15, 2006

yah if expressions and ?: are equivalent except for the ternary typically doesn't support multiple lines in the branches

you cannot be against if expressions while also being supportive of ?: because that would be contradictory

rjmccall posted:

the verbosity difference here is not huge
if it was a rare use case, I'd agree it's not a big deal. however, producing an immutable value based on conditional branching comes up so often for me that I'd like the language to have good syntax for it.

I will agree the swift example is not as atrocious as it might be in c++ or c#.

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?
what exactly is gained by having if statements and ?: over if expressions?

comedyblissoption
Mar 15, 2006

JawnV6 posted:

goes on to say it does this without imposing any runtime constraints, couple paragraphs after claiming LLVM doesn't have loops
im definitely not an expert, but rust shares the c++ philosophies of "zero-cost abstractions" and "you don't pay for what you don't use"

as an example, rust tries to make it so that iterating over a vector is just as fast as handrolling the equivalent C or assembly iteration while providing a safe and high level interface to the vector

JawnV6
Jul 4, 2004

So hot ...
i'm more interested in how it prevents data races without runtime cost, but the llvm loop thing is more about his abject unfamiliarity with llvm. there's an earlier post where an attempt to survey the field doesn't mention it.

does rust let you query such a loop to determine if invariants can be hoisted? or is that a nonsensical question because the vector formatting doesn't permit hoistable invariants anyway?

sarehu
Apr 20, 2007

(call/cc call/cc)
Rust doesn't have goto statements expressions yet, right? So that's one thing where you pay cost. Another is that user defined datatypes can't be moved when they have incoming pointers because they get moved with memcpy. Also no real exceptions?

And, borrow checking.

Basically "zero cost" here means the stuff you do has no extra cost but there's stuff you can't do.

HappyHippo posted:

what exactly is gained by having if statements and ?: over if expressions?

Not having bad semicolon rules, also not walking yourself into having break and continue be expressions, and if you want goto statements, that really requires that if/else used as a statement be distinguishable at some level from if/else being used as an expression.

sarehu fucked around with this message at 23:33 on Jul 23, 2016

crazypenguin
Mar 9, 2005
nothing witty here, move along
you can have if/switch as both statements and expressions, too. this poses no serious problems.

comedyblissoption
Mar 15, 2006

JawnV6 posted:

i'm more interested in how it prevents data races without runtime cost
rust doesn't let you dereference raw pointers unless you encapsulate it in unsafe

rust encourages heavily using references and moves

you can declare references and values as either mutable or immutable (similar to const in c++)

rust statically prevents you from aliasing mutable references

not allowing aliased mutable references helps prevent violating invariants and prevents iterator invalidations

rust uses type annotations to mark certain types as being safe to move or copy to another thread so that it can prevent unsafe moves or copies

comedyblissoption
Mar 15, 2006

here is an article w/ more details if youre interested
https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

i personally think after working on a C# codebase wrought w/ data race problems, statically guaranteeing youre not gonna gently caress up sharing data is important

Zemyla
Aug 6, 2008

I'll take her off your hands. Pleasure doing business with you!
I've used Maxima, and its semicolon works the same way as Rust's. The best way to explain it is to say that the semicolon is a postfix operator that basically means, "Don't return this value" and lets you put another expression afterwards.

redleader
Aug 18, 2005

Engage according to operational parameters
the rust semicolon thing seems like a problem that's theoretically annoying but not actually a problem in practice.
not that i've ever used rust

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
the only way that a transpiler would ever produce safe, idiomatic rust code is if the source language was basically a safe, sugared subset of rust. you can compile c to rust, but you will have to wrap every pointer dereference in an unsafe block. you can compile javascript to rust, but all of the code you produce will traffic in some JSValue enum type that includes a shared gc reference. using rust as an ir does not give the source language any of the benefits of rust

JawnV6 posted:

i'm more interested in how it prevents data races without runtime cost

rust has a lot of special checking to ensure that values are only used uniquely. like, if you have an array, you can get a pointer to an element, but now you can't use the original array anymore until you're done with the pointer; or maybe you pass the array off to someone else, and now you can't use the array anymore at all. there are a number of things that can benefit from this kind of checking. the race-condition thing is just that, if you've got something like a pointer and you know that nobody else has that pointer, you can be sure that dererencing it won't race

if you do need something like a shared reference, there are types in the library for that, but they won't give you access to the memory unless you do something like take a lock. or they'll say that accessing the memory is an unsafe operation, so it has to be done in an unsafe block, and once you do that of course you can have races again. and rust forces basically everything that could possibly be unsafe to be done in an unsafe block, like calling a c function

it's actually pretty neat, but it is an expert feature, and it does not interoperate well at all with existing systems

JawnV6 posted:

does rust let you query such a loop to determine if invariants can be hoisted? or is that a nonsensical question because the vector formatting doesn't permit hoistable invariants anyway?

iteration in rust will do things like give you direct pointer access to the elements of the vector, so that the ultimate code approximates what a c compiler would produce, but it forces it to be done in a safe way. i don't know what sort of invariants you're thinking of to answer more specifically than that

the loop thing is really weird, you are almost certain to not be able to transpile a loop from a different language into exactly a rust loop anyway

Xarn
Jun 26, 2015

rjmccall posted:

... or they'll say that accessing the memory is an unsafe operation, so it has to be done in an unsafe block, and once you do that of course you can have races again. and rust forces basically everything that could possibly be unsafe to be done in an unsafe block, like calling a c function

it's actually pretty neat, but it is an expert feature, and it does not interoperate well at all with existing systems

This is basically how I imagine a theoretical transpiler to rust would work. Start the program with a huge, fuckoff unsafe block, write everything there, done. And its also why its extremely dumb idea.

Soricidus
Oct 21, 2010
freedom-hating statist shill

comedyblissoption posted:

that makes sense

i suspect they struggled with wanting lambdas to look like:
code:

|x| x + 1
instead of
code:

|x| x + 1;
and trying to base a consistent syntax around that instead of having special edge cases

here's another solution that took me 0.00001s to think up: make the last semicolon optional. now you can have nice looking closures without adding an annoying source of compile errors!

but nope rust devs just gotta get all cute with their syntax

gonadic io
Feb 16, 2011

>>=

Soricidus posted:

here's another solution that took me 0.00001s to think up: make the last semicolon optional. now you can have nice looking closures without adding an annoying source of compile errors!

but nope rust devs just gotta get all cute with their syntax

trust me, you don't want things to auto convert to unit (which i believe is the solution you're suggesting)

scala does it and it drives me nuts

Soricidus
Oct 21, 2010
freedom-hating statist shill

gonadic io posted:

trust me, you don't want things to auto convert to unit (which i believe is the solution you're suggesting)

scala does it and it drives me nuts

i don't know scala so idk what you mean by that. auto conversions are bad. rusts idea of appending something to the list of expressions to indicate that you don't want to return a value is good. making that thing be an empty expression indicated with an easily overlooked bit of syntax that has meant something different in basically every mainstream pl for decades is bad.

the talent deficit
Dec 20, 2003

self-deprecation is a very british trait, and problems can arise when the british attempt to do so with a foreign culture





if you read the semicolon as 'andThen' instead of as an expression terminator rust's design makes sense

if you really hate it you can always insert explicit nil after the last semicolon

AWWNAW
Dec 30, 2008

wish I could convert this discussion to unit

Bloody
Mar 3, 2013

if you think about semicolons in code any harder than you think about periods in text I don't know what to say

Bloody
Mar 3, 2013

I don't read them as anything. they're just there, gluing bits of poo poo together

JawnV6
Jul 4, 2004

So hot ...

rjmccall posted:

the only way that a transpiler would ever produce safe, idiomatic rust code is if the source language was basically a safe, sugared subset of rust
im pretty sure that's the dream he alludes to, minus the context he's unaware of

rjmccall posted:

rust has a lot of special checking to ensure that values are only used uniquely. like, if you have an array, you can get a pointer to an element, but now you can't use the original array anymore until you're done with the pointer; or maybe you pass the array off to someone else, and now you can't use the array anymore at all. there are a number of things that can benefit from this kind of checking. the race-condition thing is just that, if you've got something like a pointer and you know that nobody else has that pointer, you can be sure that dererencing it won't race
i was thinking it was either casting unsafe around anything remotely contentious or coming up with a lot of rules that would hamper performant code, if not literally invoking a runtime component to deal with it

rjmccall posted:

if you do need something like a shared reference, there are types in the library for that, but they won't give you access to the memory unless you do something like take a lock. or they'll say that accessing the memory is an unsafe operation, so it has to be done in an unsafe block, and once you do that of course you can have races again. and rust forces basically everything that could possibly be unsafe to be done in an unsafe block, like calling a c function

it's actually pretty neat, but it is an expert feature, and it does not interoperate well at all with existing systems
it's odd to me that they'd be able to make something like this performant without being really hardware-aware, but that's probably my own bias

i do different tasks in different languages, but working between them hasn't been a major pain point. it might fall into the "teach not want what they don't provide" loophole, but the kind of shimming necessary to drop python string handling into C no matter what IR doesn't seem like a good ROI even if the safety brush painting magically worked

rjmccall posted:

iteration in rust will do things like give you direct pointer access to the elements of the vector, so that the ultimate code approximates what a c compiler would produce, but it forces it to be done in a safe way. i don't know what sort of invariants you're thinking of to answer more specifically than that

the loop thing is really weird, you are almost certain to not be able to transpile a loop from a different language into exactly a rust loop anyway
naively googling 'llvm loop' took me to the headers for loop operations on CFG elements. it seems like a decent set of operations i'd want if i had an IR and was mucking around with it before fixing into assembly. kinda falls into the same semantic benefit of the doubt he's giving rust elsewhere, "llvm doesn't have loops" vs "other languages won't have to implement loops because they'll use rust's"?

Wheany
Mar 17, 2006

Spinyahahahahahahahahahahahaha!

Doctor Rope

rjmccall posted:

this is a really weird question

under the hood, every programming language mutates memory. this is because computers do not come with the end-state of our programs burnt into rom

i am positive that there are haskell optimizations that find ways to modify things in-place when they recognize that that's possible, or better yet avoid creating the original data structure when it's immediately discarded. if you're imagining that that applies super-commonly outside of very local things like chaining maps and filters, you are probably mistaken

in a program that does i/o, the whole universe is state :lsd:

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

JawnV6 posted:

i was thinking it was either casting unsafe around anything remotely contentious or coming up with a lot of rules that would hamper performant code, if not literally invoking a runtime component to deal with it

it's odd to me that they'd be able to make something like this performant without being really hardware-aware, but that's probably my own bias

the exclusive access stuff doesn't require hardware awareness because you're just emitting unsynchronized accesses. things that share data between threads generally have to barrier to get transitive memory ordering of course

the shared stuff doesn't, like, get implicitly optimized to use atomic accesses or anything. there's a standard library type which allows you pass a shared reference, but when you create it you're also creating a lock, and in order to access the memory you have to acquire the lock. it is not especially performant. there are also atomic types in the library, but you have to explicitly use them instead of a locked reference, and of course they have a more modest api surface

JawnV6 posted:

naively googling 'llvm loop' took me to the headers for loop operations on CFG elements. it seems like a decent set of operations i'd want if i had an IR and was mucking around with it before fixing into assembly. kinda falls into the same semantic benefit of the doubt he's giving rust elsewhere, "llvm doesn't have loops" vs "other languages won't have to implement loops because they'll use rust's"?

llvm has an unrestricted basic-blocks-and-branches cfg, from which you can infer structure like loops, and which you can obviously lower loops to. structured control flow is hilariously low on the list of complexities that somebody writing a compiler wil have to face. (unstructured control flow, like gotos that can jump into the middle of inner blocks, is consistently annoying)

Athas
Aug 6, 2007

fuck that joker

rjmccall posted:

structured control flow is hilariously low on the list of complexities that somebody writing a compiler wil have to face.

It helps if you want to do loop optimisations, or parallelisation.

But then this terrible idea to compile to Rust won't help anyway. (And I say this as someone who's pet language compiles to C/OpenCL, not LLVM.)

Wheany
Mar 17, 2006

Spinyahahahahahahahahahahahaha!

Doctor Rope
"Spliterator" is both the best and the worst name

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Athas posted:

It helps if you want to do loop optimisations, or parallelisation.

not sure what you're saying here; i wasn't, like, arguing against the very concept of structured control flow, i was saying that it's not hard to compile and having nice builtin loop structures in the target language doesn't make much of a difference

JawnV6
Jul 4, 2004

So hot ...

rjmccall posted:

the shared stuff doesn't, like, get implicitly optimized to use atomic accesses or anything. there's a standard library type which allows you pass a shared reference, but when you create it you're also creating a lock, and in order to access the memory you have to acquire the lock. it is not especially performant. there are also atomic types in the library, but you have to explicitly use them instead of a locked reference, and of course they have a more modest api surface
an implicit lock seems really opinionated on how synchronization should be done, and I'm guessing there's no support for a try/peek operation that gets the status without blocking to acquire?

building things up from atomic primitives seems like the same point you'd be at with llvm as a target

Adbot
ADBOT LOVES YOU

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

JawnV6 posted:

an implicit lock seems really opinionated on how synchronization should be done, and I'm guessing there's no support for a try/peek operation that gets the status without blocking to acquire?

i'm apparently conflating a couple different things

there's a type in the library which is a thread-safe reference-counted thing, but you can only get mutable access to the value if the reference count is 1 (which you can dynamically query)

the mutex type builds in knowledge of the thing it protects. accessing that memory is not implicit, you call a lock method which blocks and gives you back something that you can modify. it does support a try_lock

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