amber-lang / amber

💎 Amber the programming language compiled to Bash
https://amber-lang.com
GNU General Public License v3.0
3.92k stars 86 forks source link

Rethinking failable types #478

Open b1ek opened 1 month ago

b1ek commented 1 month ago

preface

i feel like the way failables are implemented right now is completely wrong and we have to start over.

lets say that we have $curl something.com$. it returns a Text, right? and its not the data that is failable, but the eval block itself.

so right now we have failable types that are only usable in function declarations:

fun process(raw: Text): Text {
    ...
}
fun get_data(): Text? {
    let raw = $curl data.com$? // <--- this is the failable part
    return process(raw) // <--- data is Text here, not Text?
}

but imo types have nothing to do with failability. even when called, the function has to have a failed block right away and its not possible to unwrap that type later as you would do with a Result in rust.

like if i call it like this

let a = get_data()

the compiler will say "The failed expression must be followed by a block or statement". an expression. not a type

what i want to do with it

remove the failable type, and add a failable function modifier instead:

failable fun get_data() { ... }

the same way as the pub modifier works

Ph0enixKM commented 1 month ago

I was also thinking about it. Also what about actually failable types?

We could implement Rust's Result<Int,T> type. Where T is the data type and Int will always be an exit code. Then T? could be the syntax representing this new type. Additionally, we do not introduce yet another breaking change.

Text? can be some text value or hide an exit code that we can use to handle later.

I got stuck with the Bash implementation for it as Sh does not support arrays and Bash nested arrays.

b1ek commented 1 month ago

i think we could implement Result<Int, T> when, if we make custom types. and implement it as part of stdlib, not as part of the compiler, with an .unwrap() and .expect(Text) methods like rust has

b1ek commented 1 month ago

so far i see we have the following options:

  1. leave it as is, and add a ( ... ) brackets for types so that a union type could be also marked as failable
  2. add a function modifier:
    1. pub failable fun abc(): Type {}
    2. pub fun abc?(): Type {}
    3. pub fun abc()?: Type {}
  3. no modifiers, a function is failable if it has failable code in it
  4. compiler flags
b1ek commented 1 month ago

come to think of it, do we even need the modifiers? it could be set automatically if a function has a $...$? or fail in it. that would be sort of hard to tell, though

b1ek commented 1 month ago

or use the compiler flags:

#[failable_fun]
fun abc(): Type { ... }
mks-h commented 1 month ago

Let me remind you a few things:

  1. We detect failability regardless of returned type
  2. The way failability works is the same as it was for as long as I know it
  3. The ? in the function declaration is purely for readability, and doesn't actually change anything

Regardless, your ideas are all over the place — you are trying to redefine the whole language's error handling system, simply because you don't like to put parentheses around union types. Arguments about not being able to use the type anywhere, but in the return type, are not valid because nobody asked for it to be otherwise. You are only using this argument to have additional points in favor of making any change, big or small, that will solve your specific little issue — disregarding everything else.

Please file an issue which specifically targets your problem, so we can think on it. I will not participate in this logical fallacy of an issue any further.

b1ek commented 1 month ago

the thing i don't like is that types are the thing that is failable, and i want to move it to be a function modifier instead

i have thoroughly explained why i think that failable types are a bad idea in the issue. the current error handling system works as if the value might fail, but its simply not the way how it works under the hood: if the exit/return code is anything but 0, it considers to have failed. the actual data has nothing to do with the exit code.

you are trying to redefine the whole language's error handling system, because you don't like to put parentheses around union types

that's completely irrelevant in this issue, and not a constructive argument.

to solve your specific little issue - disregarding everything else

can you elaborate? what is "everything else"? what is "my specific little issue? i genuinely have no idea what are you talking about

are not valid because nobody asked for it to be otherwise

its me. im asking. and please do not use emotional language in this forum, its completely irrelevant and damages the civility of this conversation

mks-h commented 1 month ago

In case I wasn't clear enough, I'm not going to respond to your questions here. If the issues you mentioned are truly issues on their own, and not simply additional points to support your main idea (and you don't care for them otherwise), then you ought to open separate issues for them. Moreover you should present actual use cases for them. That way we can actually discuss these problems, whether they are problems in the first place, and possible solutions.

Presenting these problems in a combined issue which asks to change how everything works is counterproductive. Each language design decision has to be carefully considered, instead of copy-pasted from Rust or otherwise slapped together.