20 ms·
Be wary of functions which take several parameters of the same type
- Terr_ 7y agoIn some cases this can be fixed by introducing a new type, especially if the language makes it nice to support as a literal. This confers additional benefits in terms of type-checking. For example, changing an ambiguous foo(float x, float y) into foo(distance x, weight y) .
- tlb 7y agoI'm not a fan of the CopyFile solution suggested. Often one parameter is distinguished by whether it could be an array. The Unix cat utility takes multiple sources to a single destination, so analogously: bool cat_files_to_file(string dst, vector<string> srcs) and it'll be obvious in the call site what's going on: cat_files_to_file("/tmp/backup", {"presentation.md"}); and your function can also concatenate files.
- fennecfoxen 7y agoMany languages have some concept of "keyword arguments". (Those that don't will need to pass in named tuples, or structs, or similar data structures.)
- agwa 7y agoHere's a variation of the example which I like a bit better: type CopyFile string func (src CopyFile) To(dest string) error { // copy file here } func main() { CopyFile("presentation.md").To("/tmp/backup") }
- shhsshs 7y agoNice, very clever!
- jonahx 7y agoThe problem is, even though it reads smoothly, the names are no longer accurate. The smoothness comes from reading "CopyFile" as a verb (semantically), but "CopyFile" is actually a noun (syntactically). Source("presentation.md").CopyTo("/tmp/backup") while scoring slightly fewer slickness points, wins overall imo because it remains accurate.
- thinkingkong 7y agoIf we're going to use types to handle this then I'm a fan of the Go method of Writer and Reader. That way, even if you have a method named copy, you cannot really mix the two up. Writer restricts the operations; same as Reader. Having method names imply direction or argument order is gonna be tricky once you add a couple people to your project.
- shhsshs 7y agoThat breaks down when both src and dst implement io.ReadWriter. os.File for example. I had an adventure upgrading versions of the .NET Core Rabbit library: one of the (boolean) parameters of the Consume function used to be “noAck”, but it got changed to “autoAck” (with inverted behavior). Even though the change was well documented, I would have preferred that to be a breaking change at compile time...
- thelazydogsback 7y agoKW's: CopyFile(from: 'foo', to: 'bar') or JS: CopyFile({from: 'foo', to: 'bar'}) or as types: CopyFile(Source('foo'), Dest('bar')) or as fluent: Copy.from('foo').to('bar')
- toyg 7y agoor as Python: shutil.copy(src='foo', dst='bar') That's straight out of stdlib, btw.
- echelon 7y agoThose are keyword args and are not specific to just Python. :) The parent post outlines many techniques, including this. Some are more apt than others depending on your language of choice. I would argue that there's another option of passing a strongly typed struct for args, and that the JavaScript and Ruby solution of dicts falls between this and kwargs. I'm fond of properly designed fluent APIs, myself. While on the subject of homogenous argument types, especially bad are the APIs that not only accept the same types, but have order semantics that differ between functions within the same library. PHP used to be a notoriously bad example of this with respect to their needle and haystack search parameters. (I think they fixed it?)
- toyg 7y agoYeah, I was just showing that in Python keyword arguments are first-class in every respect, which is not that common. The "JS solution" for example is not a solution, but only a convention: the actual interface is just expecting an object, and there is no explicit check on the validity of such object or its contents. That is nowhere near "strongly typed", if anything it's weaker than average.
- thelazydogsback 7y ago> The "JS solution" for example is not a solution, but only a convention: the actual interface is just expecting an object, Well if it uses de-structuring syntax, then it can be type-checked.
- salmonellaeater 7y agoDestructured object parameters make this easy in TypeScript (and JS): async function copyFile({ source, dest, }: {source:string, dest:string}): Promise<void> { // ... } await copyFile({source: 'presentation.md', dest: '/tmp/backup'});
- yawaramin 7y agoThere's no need to destructure the parameters.
- julik 7y agoA language with keyword arguments helps against this. Crystal has a great compromise IMO where you can call the same arguments positionally and as keywords based on the argument name.
- wildlogic 7y agosame goes with python - def foo(x=None, y=None): ... foo(x=1, y=2) foo(1, y=2) foo(1, 2)
- DonaldPShimoda 7y agoYou can also disable positional handling via * to require keywords in function calls, e.g.: def add(*, l, r): return l + r add(l=1, r=2) >>> 3 add(1, r=2) >>> [...] >>> TypeError: add() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given I believe 3.8 is adding / (I think) to do the same but for positional arguments, i.e., arguments you cannot give by keyword.
- echelon 7y agoJust be careful not to use mutable objects as your default args.
- XorNot 7y agoThe problem with keywords is the compiler can know nothing of intent with them - consider: CopyFile(from: loaded_file, to: report_file) Now, somewhere else off in the code someone does something like: // loaded_file is now the report - so no worries. loaded_file = report_file And the compiler then happily propagates that. At no time does it throw an error saying something which would be quite informative such as "Cannot assign report_file (type: outputFile) to loaded_file (type: sourceFile). Which is really what we want to have happen because - at the very least it forces the developer to go inspect the usage sites to figure out if what they're doing makes sense.
- jph 7y agoIMHO this concept is better solved by using semantics, such as types, keyword parameters, interfaces, traits, etc: copy(source: ReadOnlyFile, destination: WriteOnlyFile) These all help make the code more semantic, more testable, and more securable. These can also improve more kinds of programming, such as a function that truly needs multiple parameters of the same type (e.g. iterators that are not commutative) or a function that benefits from security aspects (e.g. read-only data).
- GhettoMaestro 7y agoFor a second, based on the domain, I wondered if Dick Cheney had re-invented himself as a developer.
- ppod 7y agoA nice thing about "everything is an object" languages is that a.isGreaterThan(b) seems less ambigous than isGreaterThan(a,b). There are natural langauges that are Verb-Subject-Object. It seems less common in natural language to have neither argument be an "agent". Is the copula in the natural language doing the work of the dot?
- gowld 7y agoThat doesn't scale past two arguments.
- ben509 7y agoYou can use fluent interfaces to get past two args: myObject.doThing(a).toAthing(b) Though that requires a good amount of boilerplate on the part of the library author.
- ppod 7y agoMary.give(apple).to(John) Mary.give(John, apple)
- reuben364 7y agoAdga does mixfix pretty well. A variable in scope can indicate the places for arguments with underscores (e.g. _+_ or _=<_>_). The tradeoff for being able to use pretty much any symbol in identifiers along with mixfix is that you have use whitespace (3 + 5, not 3+5). Though I agree it doesn't scale.
- wereHamster 7y agoIn Haskell you can make any (prefix) function infix (and vice versa). Some other languages (eg. Kotlin) support this as well. a + b -- '(+)' function used infix (+) a b -- '(+)' function used prefix min a b -- 'min' function used prefix a `min` b -- 'min' function used infix
- jodrellblank 7y agoIn natural language, "A isGreaterThan B" is a statement and reads as an assertion or a constraint, instead of a conditional test or question; it's ambiguous whether it should do anything. Esperanto has a flexible word order; I think the options would be: ĉu A estas pli granda ol B? (S-V-O) (lit. "whether A is more big than B?") ĉu A pli granda ol B estas? (S-O-V) ĉu estas A pli granda ol B? (V-O-S) and never "estas pli granda ol A B" as you suggest, but also never avoiding any indication that it's a question. From a natural language side, it feels very contrived and arbitrary to have one hill measure whether it is bigger than another hill, instead of having an observer measure both 'from the outside'. That suggests the imbalance in syntax between a. and (b) is strange. a > b is more balanced and looks more like an outsider doing the comparison.
- h2odragon 7y ago> Can you suggest a better solution? Yeah. Mind your docs. Languages and APIs designed like this and get you crippled tools that simply do not allow you to express about half the shit you're gonna need to do. That generates another layer of indirection, abstractions, and workarounds to break through the "we think for you" for when what "they thought for you" wasn't what you was actually thunking. There's a place for languages that don't allow you to shoot yourself in the foot or other member of your choice, I'm sure. I don't want to go there.
- bitwize 7y agoThe idea is to use the type system to help you check your work. If programmers were anywhere near diligent about minding the docs, static typing would be far less useful than it is. Always assume the programmer hasn't had their coffee and is prone to making mistakes, and design your system to catch them as early as possible.
- jonahx 7y ago> Languages and APIs designed like this and get you crippled tools that simply do not allow you to express about half the shit you're gonna need to do. What problems would you see arising (either directly or indirectly) from the specific suggestion in the article, for example?
- kelnos 7y agoIf an API requires that I re-check the docs to remind me of something simple like whether or not the source or destination comes first, the API is bad. This isn't about removing flexibility, it's about helping people to avoid making easy mistakes that could be prevented.
- jaequery 7y agoThe solution provided by OP is what I try to avoid at all costs. It’s the beginning of spaghetti code.
- radiator 7y agoI agree, and I am not still sure if he is serious.
- micah_chatt 7y agoThis. It extends to command line tools too. I can _never_ remember what the order is for `ln -s`.
- btrettel 7y agoI remember the order for ln -s because the third argument is optional. If you omit the third argument the command will create a symbolic link in the current directory with the same filename as the original.
- rabidrat 7y agoI remember this usage the exact same way :)
- saghm 7y agoUnix tools are pretty consistent that source comes before destination. If you get stuck, just remember that it's the same as cp, mv, etc.
- hibbelig 7y agoI think the confusion comes because you have to do ln -s bar foo so that "ls -l" then prints foo -> bar People who look at an existing symlink before trying to create a new one will get confused. Of course, the solution is to remember that ln is in the same family as cp and mv; ls is a different family.
- baby 7y agothat’s one of the reason why I like languages that allow you to write this: thing(dest=a, src=b
- nostrademons 7y agoIntelliJ labels the parameter at the call site with its parameter name, if its value isn't a local variable that already has that name. On your display it looks like CopyFile(from: "foo", to: "bar"), with the from and to labels in an unobtrusive font, but in the source file it's just CopyFile("foo", "bar").
- Semaphor 7y agoNot just IntelliJ, all their IDE’s
- Stratoscope 7y agoWhen someone says "IntelliJ" they are often referring to any and all of the JetBrains IDEs. They are all slightly different versions of IntelliJ.
- danfang 7y agoYou don't want it unlabeled in the source code though - a code reviewer will totally miss a swapping of parameters. It's best when fully supported and explicit in the language.
- sk5t 7y agoYup! It's nice and all that IntelliJ ghosts the parameter names in there, but that does nothing for external review, static analysis, etc.
- s17n 7y agoPositional arguments just shouldn't exist. Why they persist in newer languages like Go is a mystery.
- ben509 7y agoIt's due to how language and entropy work; basically people naturally tend to abbreviate frequently used terms. Some languages have banned positional arguments, like Obj C, but these do get a reputation for being verbose. And verbosity, past a point, hinders readability. My inclination is that for unary and binary functions, positional arguments should be sugar for the keyword arguments, and anything beyond that must be keywords. An alternative rule could be you're allowed to designate one positional argument. Keep in mind, also, that I'll bet there's code in keyword-only languages that just uses single letters for all the keywords. Some people just won't or can't write readable code.
- s17n 7y agoUnary, yes. Binary, no.
- deleted 7y ago[deleted]
- rq1 7y agoIn C++ one could use strong types (template phantom types with anonymous tags) to distinguish between source and destination. I use this technic to disambiguate all kind of types (source/destination gpu/cpu pointers for instance) and correctly dispatch everything at compile time. The compiler becomes your best friend then. :)
- archi42 7y agoThat's a nice idea. Also, while I love C++, I'm really frustrated that there are no native named arguments :( However, after your comment, I decided to spend a few minutes on the topic, and came up with these links: https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/ https://www.fluentcpp.com/2016/12/08/strong-types-for-strong... https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/ https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/ I am now looking forward to refactor some of our code base to make use of this. Awesome and thanks for the nudge!
- thrower123 7y agoThis is really the number one reason I'd like to use F# more. Single case unions and units of measure are really slick, and while you can do the same thing in C#, the legwork is pretty high.
- louthy 7y agoIn my language-ext library it's pretty trivial: class Hours : NewType<Hours, double> { public Hours(double x) : base(x) {} } It gives you: * Equality operators, Equals, and IEquatable * Ordering operators, CompareTo, and IComparable * GetHashCode, ToString * Map, Select, Bind, SelectMany, Fold, ForAll, Exists, Iter * Explict conversion operator to convert from the NewType to the internal wrapped type * Serialisation * Hours.New(x) constructor function (often useful when used with other methods that take first-class functions). It's even possible with the more complex variants to provide predicates that will run on construction to constrain the values going in. There's also NumType for integer numeric types like int, long, etc. and FloatType for floating-point numeric types like float, double, etc. They give additional functionality by default. Also, units of measure [4]: Length x = 10*inches; Area y = x * x; Time t = 10*sec; [1] NewType - https://github.com/louthy/language-ext/tree/master/LanguageExt.Core/DataTypes/NewType https://github.com/louthy/language-ext/tree/master/LanguageE... [2] NumType - https://github.com/louthy/language-ext/tree/master/LanguageExt.Core/DataTypes/NumType https://github.com/louthy/language-ext/tree/master/LanguageE... [3] FloatType - https://github.com/louthy/language-ext/tree/master/LanguageExt.Core/DataTypes/FloatType https://github.com/louthy/language-ext/tree/master/LanguageE... [4] Units of Measure - https://github.com/louthy/language-ext/tree/master/LanguageExt.Core/DataTypes/UnitsOfMeasure https://github.com/louthy/language-ext/tree/master/LanguageE...
- thelazydogsback 7y agoOf course F# has units-of-measure built-in
- louthy 7y agoErm, no it doesn't. It has the ability to define units of measure, but the language doesn't have UoM built-in. There are UoM in FSharp.Core [1], so it's the same as including it from a library. Obviously F#’s first class support for UoM is better than my implementation, but for all intents and purposes the behaviour is the same. [1] https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/SI.fs https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSha...
- axaxs 7y agoThere was a post here not long ago, by jerf iirc, called something like 'tiny types in go.' At the time I disagreed with the premise, but I think it would be a cleaner solution here. By aliasing two string types with obvious names, it forces you to convert at calltime. Assuming you name source and dest as types(ie type source string), you end up with a command like copy(source(sourcefile), dest(destfile)). It's cleaner and has less abstraction than what's in the article.
- acjohnson55 7y agoThere are also techniques like type tags and refinement types in some programming languages, which serve a similar effect. It's really useful for marking data as validated or as earmarked for a given purpose as it flows through layers of a system.
- pezo1919 7y agoI don't get why this got upvoted.
- hprotagonist 7y agoa way to avoid this in python that i quite like is to require keyword arguments: def foo(*, x, y): pass # you must do this foo(x=5, y=6) # you cannot do this foo(5,6) # and therefore you also may not foo(6,5) This is a little autocratic but the other nice thing is that if you’re finding it really painful to use it means you should probably refactor anyway.
- XJ6 7y agoI prefer to let the IDE do this. Developing C# in rider if you call `foo(a,b)` then it'll display as `foo(from: a, to: b)` in the IDE.
- throwaway744678 7y agoAll Jetbrains IDE do this (AFAIK). One can even configure the cases when these are displayed (ie. only show the hints when there is an ambiguity)
- hprotagonist 7y agofor my own uses, that's great. I write my libraries like this because i want to beat my users into being explicit -- thus the "a little autocratic, but.." caveat.
- hinkley 7y agoI used to feel guilty for how much I use Google, developer.mozilla.org and Stack Overflow, but after a series of languages and a herd of APIs, it's a blur to remember which order is correct for which library, and how they deal with inputs that are empty or undefined. It's safer to look it up, peruse the complaints about how it does weird shit with negative numbers or empty string, and then write my code standing on those shoulders. I spend enough time stepping through other people's code even when I do this stuff. It's really not that interesting and certainly not fun.
- taneq 7y agoAmen to that. And it goes double when you're hopping around different parts of a multidisciplinary project and probably only using a given language or framework once or twice a year for a couple of weeks. These days I find myself looking things up just on principle, even when I'm reeeasonably sure I remember how they work. It just saves time in the long run.
- hinkley 7y agoI had a project with three implementations of merge (including lodash) and they all worked differently. After I fixed a handful of other people’s bugs due to using one and expecting the behavior of another, I felt a lot less guilty.
- jdm2212 7y agoErrorProne has a nice safeguard against this if you're writing Java code: https://github.com/google/error-prone/blob/master/docs/bugpattern/ParameterName.md https://github.com/google/error-prone/blob/master/docs/bugpa... It detects if your function signature is > foo(first, second) and you call it with > foo(second, first) that is, it actually checks for variable names that are mismatched, with comments like > foo(/* first= * / second, /* second= * / first) as an escape hatch.
- jniedrauer 7y agoTo be honest, I prefer the simple form. It's immediately obvious what it does, and the cognitive load when working with it is slightly lower. That justifies the extra second or so you need to view the function signature and make sure your arguments are in the right order. "Clever" solutions to anticipated problems that haven't happened yet frequently end up being tech debt down the line.
- zwieback 7y agoFor me Intellisense pretty much solves this problem
- deleted 7y ago[deleted]
- eternalny1 7y agoThis is one example of why working in a higher-level language, if you can for a given task, makes life easier. C#: PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");
- alkonaut 7y agoYour junior colleague would still do PrintOrderDetails(1234, "SomeSeller", "SomeProduct"); And the compiler wouldn't notice. You could uused a Roslyn analyzer that gives a warning "methods with consecutive args of the same type should use named params" I guess. Or a warning could be made at the declaring site "Don't declare methods with consecutive args of the same type".
- jonahx 7y agoThere's a lot of hate in the Go community for languages like Haskell, and a lot of pride in Go for "keeping it simple" and "staying out of your way." Yet if you watch Go conference videos or read Go blogs, you'll see quite a few idioms like this one, which boil down to just wanting to use real types for things, instead of layering semantic meaning onto strings or ints or nils, and keeping that meaning in your head (or in the docs).
- deleted 7y ago[deleted]
- broth 7y agoI would be wary of functions that take several parameters regardless if they are of the same type -- at that point you're already going to have to look into the documentation.
- eccbits 7y agoDoes anyone else feel this is... ridiculous? In that “at some point, you do have to start paying attention to the code you’re writing” way?
- samvelst 7y agoI feel the same way sometimes too. One thing to consider though is that when the overall complexity of a project increases these little things do add up.
- kstenerud 7y agoWhen it's impossible to avoid ambiguity in the parameters, you can just chose a function name that makes the ordering explicit: copy_from_to(file from, file to)
- raxxorrax 7y agoI like this more than being forced to name parameters since that would be too verbose in many applications. But there is a danger that the label of a type becomes less generic. Vector3(...) VectorXYZ(...) The second variant could be weird depending on application. Still, I see some people argue that positional arguments should be forbidden in newer languages but I would disagree and my main argument would indeed be laziness.
- emmelaich 7y agoThe Unix/Linux command line is unfortunate like this; the cp/mv/ln commands would be better off with the destination specified specially. For instance gnu cp allows the syntax cp -t <dir> <source...> Which is good because it makes it similar to most other commands. It would be unfortunate to mandate it though; it would be unfortunate for an `option` to be mandatory.
- justincormack 7y agoIt also allows you to use xargs to pipe a list of files in.
- molyss 7y agoWhile it's not a perfect example or solution, this illustrate a fairly common issue with modern code. He also mentions too many parameters. Other commonly frown upon constructs include unclear names (java "filter" or "compareTo" easily come to mind), boolean parameters (One should use named enums instead). Some add to the list out parameters (passing a to-be-modified list as a parameter), functions returning their modified input parameter, even though they certainly have their usefulness (like being able to handle memory management outside of the callee). I'd personally like to add String parameters to the list, but modern language make such restrictions often too cumbersome. I the given example, I'd love to see a language with optional named parameters. For the function copy(String fromFileName, String toFileName), instead of calling copy (file!, file2), one would have the possibility of calling copy(from:file1, to:file2)
- darkfire613 7y agoYour last point is something that I really like about the Swift language. All parameters have names that are mandatory when calling the function, so in Swift you would be required to call copyFile(from: file1, to: file2). [0] 0: https://docs.swift.org/swift-book/LanguageGuide/Functions.html#ID166 https://docs.swift.org/swift-book/LanguageGuide/Functions.ht...
- Kuinox 7y agoIn C# you can optionaly name parameters when you call a method, sometime this is mendatory when you want to set the second(or more!) optional argument, but you don't want to set the first optional argument.
- js8 7y agoTLDR: Be wary of noncommutative algebras! In particular, if you have a noncommutative operator, do not denote it with a symbol that is symmetric along its vertical axis.
- toolslive 7y agoIn math, you have: matrix multiplication, vector cross product, ... So where does this idea originate?
- acjohnson55 7y agoIn linear algebra, you typically use juxtaposition to represent multiplication, rather than explicitly with a symbol. For some reason, this actually seems to capture noncommutativity better than a symmetric symbol. Another example would be string concatenation, which people often debate the merits of using a + to represent.
- js8 7y agoI guess it originated from my synthesis of the article. The article gives an example: copy() bad, copyFrom() good. The single word "copy" doesn't imply which side is which, it is kinda "symmetric". However "copyFrom" does show the side, it is kinda "asymmetric". I just considered symbols instead of words as method names. (After all, if we are to make things shorter.. TLDR is a double joke here.) And yes, you're correct, in mathematics, this is often not the rule. It's not even a rule in Haskell. But perhaps it could be a rule, at least for new operators?
- on_and_off 7y agoin kotlin, named parameters are a good defense against this. They help in many cases. Mixing named and default parameters in a method, modifying an API without breaking its usage, etc. It is slightly more verbose, but I tend to use them a lot. Basically everywhere unless the params are obvious (max(a;b) is a good example)
- thaumasiotes 7y ago> Basically everywhere unless the params are obvious (max(a;b) is a good example) This isn't so much "obvious" as "irrelevant"; the arguments to max are all exactly equivalent -- their order can't affect the result.
- thaumasiotes 7y agoExpanding on the point a little further, it would be normal from certain mathematical perspectives to see max as a unary function, mapping an unordered (multi)set to the set's maximum element. With one argument, order can't matter because only one order is possible.
- z3t4 7y agoMost of these have conventions. For copy, move, etc the convention is from target, to destination. And an IDE will show you the parameter names. If you still get it wrong, there is also no guarantee that you would assign the correct type either. eg. mixing up foo and bar in {from: foo, to: bar} or foo:From, bar:To Also if there is any speculation you should always read the source code of the function you are calling, or read the documentation, and try the function in a REPL to see how it behaves. There is a line where over- verbosity will have the opposite effect eg. making the code harder/slower to comprehend. So keep it clean and simple. If people get it wrong a lot, make the function figure out the parameters so that it doesn't matter what order you pass them in. And have the function throw a friendly human readable error if you get the arguments wrong! JavaScript example: function copy(from, to, overwrite, cb) { if(typeof overwrite == "function" && cb == undefined) { cb = overwrite; overwrite = false; } if(from == undefined) throw new Error('First argument "from" not specified!'); if(to == undefined) throw new Error('Second argument "to" not specified!'); if(!exist(from)) throw new Error("from=" + from + " does not exist!"); if(exist(to) && !overwrite) throw new Error("to=" + to + " already exist!"); ... }
- friend-monoid 7y ago* Not all users have IDEs with all that much help (vim, emacs users come to mind), and not all platforms have this ability (consider for example writing serverless functions in an online IDE), and sometimes you do code reviews from an online platform or from your phone, and sometimes you just kinda pull up a piece of code in notepad, and I think you should be able to tell that the code is obviously correct in all of those cases. * In C, the convention is "move(destination, source)". See memcpy, snprintf, strftime, others, but not all functions follow this convention. PHP is known for having lots of functions in standard library with differing conventions. * It's easier to tell that "move(from=X, to=Y)" is correct than "move(X, Y)" though? * I agree with the over-verbosity, but your last point that "annotating your arguments makes your code over-verbose and harder/slower to comprehend; instead, put all these guards and magic in the definition of the function" - isn't that also making your code harder/slower to comprehend, but this time you're putting the mindwork in understanding the function instead?
- arcticbull 7y agoIMO this is totally solved with named parameters. Nobody’s gonna mess up CopyFile(from:a, to:b). Nobody.
- mrjohnes 7y agoJust write a test.
- longemen3000 7y agoUsing key-value pairs? (Julia) `copy(src=>dest)` The advantage of that notation in this case is that expresses an explicit non-conmutability of the arguments.
- gsaga 7y agoWindow XCreateWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes);
- biztos 7y agoI love Golang but I often get "copy" wrong because it's the opposite of UNIX: copy(dst, src) -- what?! https://golang.org/pkg/builtin/#copy https://golang.org/pkg/builtin/#copy
- andreygrehov 7y agohttps://www.geeksforgeeks.org/memcpy-in-cc/ https://www.geeksforgeeks.org/memcpy-in-cc/
- nottorp 7y agoSomething about architecture astronauts comes to mind...
- rzwitserloot 7y agoThe solution (to me anyway) requires BOTH that parameters must be named, as well as cultivating a culture of creating types aggressively (and, together with that, a language that makes it easy to do this, to avoid the onus of making types leading API developers to make the wrong choice). Instead of doing: downloadFile(String url, String filePath) which runs into the ordering issue raising in the article, if it was: downloadFile(Url from, FilePath to) you no longer have the issue. But, having some way to name the parameters, which can take _many_ forms works just as well. It would help a lot if the language can enforce that you must do this: named params: downloadFile(from = "https://something/foo.txt", to = "/some/path") objC style where the function name is split up: downloadFileFrom: "https://something/foo.txt" to: "/some/path"; same as above, but with fluent-style intermediates: download().from("https://something/foo.txt").to("/some/path"); pass an object (Javascript): downloadFile({from: "https://something/foo.txt", to: "/some/path"); these all work just as well. And nevertheless, this: download(server, file) is cleaner than all of the previous attempts, and allows passing these concepts around helper methods and the like. So, _IF_ it makes sense to change the types of the arguments so that you no longer have the same type for different arguments, it seems preferable to me. However, in the case of CopyFile, to take that to its logical conclusion, you'd have a separate type to represent a 'from file' and a 'to file', but that seems too much of a stretch: The notion of a file fundamentally doesn't have a 'direction' concept built into it; that makes sense only for a copy or move API. Making separate types to represent the tuple of [direction, filepath] seems like a hacky way to introduce named parameters. So, have both.
- collyw 7y agoOr just use keyword arguments in Python. I am debugging someone else's code today and it make it so much easier when you have named keyword arguments - which he doesn't. :(
- acjohnson55 7y agoThere was an experimental language I can't remember where arguments were only named by their type by default, rather than having identifiers. This implicitly discourages having multiple parameters of the same type. After I fully processed the idea, I came to like it quite a bit.
- rmtech 7y agoSomeone should introduce a language feature - maybe a decorator - where arguments have to be called by name. @mustnameparams copy(str from, str to) copy(from = "blah", to = "hrg") > works copy("blah", "hrg") > throws error
- newtempaccount 7y agoyou mean something like def pythonDoesThis(self, *, from='':AnyStr, to='':AnyStr)->AnyStr:
- mpweiher 7y agoWith Polymorphic Identifiers: file:bar := file:foo. Let functions compute return values, use assignment if you want to move data. References: https://dl.acm.org/citation.cfm?id=2508169 https://dl.acm.org/citation.cfm?id=2508169 https://www.hpi.uni-potsdam.de/hirschfeld/publications/media/WeiherHirschfeld_2013_PolymorphicIdentifiersUniformResourceAccessInObjectiveSmalltalk_AcmDL.pdf https://www.hpi.uni-potsdam.de/hirschfeld/publications/media...
- otakucode 7y agoMy suggestion of a better solution would be better tools. For instance, when viewing or editing the code, a call to CopyFile should appear something like CopyFile([from] 'presentation.md', [to] '/home/presenter') with the "[from]" and "[to]" parts not being actual tokens that are part of the source file, but annotations that appear to assist the developer. They would appear as little badges, non-editable or selectable, with distinctions in their display to make it clear they are UI elements rather than part of the source code.
- linuxftw 7y ago> You can’t tell without consulting the documentation. Referring to the documentation is what you should be doing the first time you use a function, right? Most IDEs now have handy tool-tips to display parameter details. I use named parameters when available, but it's no big deal to pass parameters to a function. This is why we write tests.
- deleted 7y ago[deleted]
- AzzieElbab 7y agoI constantly tag primitives in scala. It is boilerplatey even with shapeless, but worth it. Tagging classes might be a bit too much though