nim-lang / RFCs

A repository for your Nim proposals.
136 stars 26 forks source link

Deprecate named argument overloading #406

Closed haxscramper closed 3 years ago

haxscramper commented 3 years ago

An experimental feature, which allow declaring procedures with identical signatures, but different argument names:

proc test(a: int) = echo "called a"
proc test(b: int) = echo "called b"

# ok
test(a = 12)
test(b = 12)

# ambiguous call error
test(12)

This feature is used very infrequently - I was able to find only 131 procedure declarations (60 groups) from 300842 processed procedures (script) - approximately 0.04% of declarations use this feature. Full list of findings included in details. I considered only type names, which means that proc[T: SomeInteger](arg: T) and proc[T: string | char](arg: T) are placed in the same group, so the actual number is even lower. Processing was done per-file.

```nim ## nimgame2 > collide ["LineCollider", "GroupCollider"] > ["d", "g"] :: nimgame2/private/collider.nim:570:0 > ["p", "g"] :: nimgame2/private/collider.nim:682:0 ## roots > showTracks ["Tracks[T, S]", "A"] > ["l", "M"] :: src/roots/findZero.nim:317:0 > ["s", "M"] :: src/roots/findZero.nim:328:0 ## roots > initState ["A", "CF", "(T, T)"] > ["meth", "fs", "x"] :: src/roots/bracketing.nim:88:0 > ["M", "f", "xs"] :: src/roots/bracketing.nim:776:0 > initState ["A", "proc (a: T): S", "(T, T)"] > ["meth", "fs", "x"] :: src/roots/bracketing.nim:102:0 > ["M", "f", "xs"] :: src/roots/bracketing.nim:816:0 ## neverwinter > fromJson ["T", "JsonNode", '' (empty string)] > ["t", "j", "flags"] :: private/jser.nim:98:0 > ["t", "j", "flags"] :: private/jser.nim:107:0 > ["t", "j", "flags"] :: private/jser.nim:116:0 > ["t", "j", "flags"] :: private/jser.nim:125:0 > ["t", "j", "flags"] :: private/jser.nim:134:0 > ["v", "j", "flags"] :: private/jser.nim:153:0 ## ngui > right_justify ["PMenuItem"] > ["a"] :: src/backends/private/gtk2/gtk2.nim:12290:0 > ["menu_item"] :: src/backends/private/gtk2/gtk2.nim:12317:0 ## gdnim > keys ["Dictionary"] > ["self"] :: deps/godot/core/dictionaries.nim:67:0 > ["dict"] :: deps/godot/core/dictionaries.nim:97:0 > values ["Dictionary"] > ["self"] :: deps/godot/core/dictionaries.nim:70:0 > ["dict"] :: deps/godot/core/dictionaries.nim:102:0 ## gdnim > toVariant ["T"] > ["val"] :: deps/godot/nim/godotnim.nim:600:0 > ["self"] :: deps/godot/nim/godotnim.nim:641:0 > ["t"] :: deps/godot/nim/godotnim.nim:829:0 > fromVariant ["T", "Variant"] > ["self", "val"] :: deps/godot/nim/godotnim.nim:514:0 > ["self", "val"] :: deps/godot/nim/godotnim.nim:609:0 > ["self", "val"] :: deps/godot/nim/godotnim.nim:644:0 > ["self", "val"] :: deps/godot/nim/godotnim.nim:651:0 > ["self", "val"] :: deps/godot/nim/godotnim.nim:659:0 > ["s", "val"] :: deps/godot/nim/godotnim.nim:787:0 > ["t", "val"] :: deps/godot/nim/godotnim.nim:839:0 ## nim-sys > kevent ["FD", "openArray[Kevent]"] > ["kq", "changeList"] :: src/sys/private/syscall/bsd/kqueue.nim:232:0 > ["kq", "eventList"] :: src/sys/private/syscall/bsd/kqueue.nim:236:0 ## cbor > writeCbor ["Stream", "object"] > ["str", "o"] :: src/cbor.nim:433:0 > ["str", "obj"] :: src/cbor.nim:443:0 ## nimph > del ["Group[K, V]", "K"] > ["group", "name"] :: src/nimph/group.nim:52:0 > ["group", "url"] :: src/nimph/group.nim:57:0 ## spotify > changePlaylistDetails [": SpotifyClient | AsyncSpotifyClient", "string", "bool", '' (empty string), '' (empty string)] > ["client", "playlistId", "public", "name", "description"] :: src/spotify/playlists.nim:75:0 > ["client", "playlistId", "collaborative", "name", "description"] :: src/spotify/playlists.nim:85:0 ## spotify > unmarshal ["JsonUnmarshaller", "JsonNode", "T"] > ["unmarshaller", "node", "v"] :: src/spotify/objects/internalunmarshallers.nim:43:0 > ["unmarshaller", "node", "data"] :: src/spotify/objects/internalunmarshallers.nim:89:0 ## dimscord > editMessage ["RestApi", "string", "string", '' (empty string), '' (empty string), '' (empty string), '' (empty string)] > ["api", "channel_id", "message_id", "content", "tts", "flags", "embeds"] :: dimscord/restapi/message.nim:85:0 > ["api", "channel_id", "message_id", "content", "tts", "flags", "embed"] :: dimscord/restapi/message.nim:105:0 ## jser > fromJson ["T", "JsonNode", '' (empty string)] > ["t", "j", "flags"] :: jser.nim:100:0 > ["t", "j", "flags"] :: jser.nim:109:0 > ["t", "j", "flags"] :: jser.nim:118:0 > ["t", "j", "flags"] :: jser.nim:127:0 > ["t", "j", "flags"] :: jser.nim:136:0 > ["v", "j", "flags"] :: jser.nim:157:0 ## godot-nim > keys ["Dictionary"] > ["self"] :: godot/core/dictionaries.nim:57:0 > ["dict"] :: godot/core/dictionaries.nim:87:0 > values ["Dictionary"] > ["self"] :: godot/core/dictionaries.nim:60:0 > ["dict"] :: godot/core/dictionaries.nim:92:0 ## godot-nim > toVariant ["T"] > ["val"] :: godot/nim/godotnim.nim:598:0 > ["self"] :: godot/nim/godotnim.nim:639:0 > ["t"] :: godot/nim/godotnim.nim:826:0 > fromVariant ["T", "Variant"] > ["self", "val"] :: godot/nim/godotnim.nim:512:0 > ["self", "val"] :: godot/nim/godotnim.nim:607:0 > ["self", "val"] :: godot/nim/godotnim.nim:642:0 > ["self", "val"] :: godot/nim/godotnim.nim:649:0 > ["self", "val"] :: godot/nim/godotnim.nim:657:0 > ["s", "val"] :: godot/nim/godotnim.nim:784:0 > ["t", "val"] :: godot/nim/godotnim.nim:836:0 ## rapid > init ["Body", "Vec2f", "float32"] > ["body", "size", "mass"] :: src/rapid/physics/simple.nim:83:0 > ["body", "size", "density"] :: src/rapid/physics/simple.nim:89:0 > newBody ["Vec2f", "float32"] > ["size", "mass"] :: src/rapid/physics/simple.nim:95:0 > ["size", "density"] :: src/rapid/physics/simple.nim:101:0 > newBody ["Vec2f", "float32", "U"] > ["size", "mass", "user"] :: src/rapid/physics/simple.nim:107:0 > ["size", "density", "user"] :: src/rapid/physics/simple.nim:115:0 ## rapid > applyImpulse ["Body", "Vec2f", '' (empty string)] > ["body", "impulse", "localPoint"] :: src/rapid/physics/chipmunk.nim:344:0 > ["body", "impulse", "worldPoint"] :: src/rapid/physics/chipmunk.nim:351:0 ## stdext > fieldItem ["T"] > ["v"] :: stdext/json_ext.nim:200:0 > ["obj"] :: stdext/json_ext.nim:255:0 ## nim-libp2p > write ["VBuffer", "PrivateKey"] > ["vb", "seckey"] :: libp2p/crypto/crypto.nim:994:0 > ["vb", "sig"] :: libp2p/crypto/crypto.nim:999:0 ## nim-libp2p > getValue ["ProtoBuffer", "ProtoHeader", "T"] > ["data", "header", "outval"] :: libp2p/protobuf/minprotobuf.nim:424:0 > ["data", "header", "outBytes"] :: libp2p/protobuf/minprotobuf.nim:494:0 ## hmisc > toPUarray ["T"] > ["p"] :: src/hmisc/wrappers/wraphelp.nim:129:0 > ["r"] :: src/hmisc/wrappers/wraphelp.nim:133:0 ## bioView > colorize [": string | char", "int"] > ["str_in", "color_fg"] :: src/color_atla.nim:111:0 > ["str_in", "color_bg"] :: src/color_atla.nim:115:0 ## zero_functional > items ["T"] > ["a"] :: zero_functional.nim:426:0 > ["f"] :: zero_functional.nim:431:0 > ["f"] :: zero_functional.nim:436:0 ## nimtraits > kindTypeName ['' (empty string)] > ["xsd"] :: src/nimtraits/xml_to_types.nim:74:0 > ["gen"] :: src/nimtraits/xml_to_types.nim:181:0 > bodyTypeName ['' (empty string)] > ["xsd"] :: src/nimtraits/xml_to_types.nim:75:0 > ["gen"] :: src/nimtraits/xml_to_types.nim:182:0 ## cligen > msplit ["MSlice", "seq[MSlice]", '' (empty string), '' (empty string), '' (empty string)] > ["s", "fs", "sep", "n", "repeat"] :: cligen/mslice.nim:188:0 > ["s", "fs", "seps", "n", "repeat"] :: cligen/mslice.nim:194:0 > msplit ["MSlice", '' (empty string), '' (empty string), '' (empty string)] > ["s", "sep", "n", "repeat"] :: cligen/mslice.nim:191:0 > ["s", "n", "seps", "repeat"] :: cligen/mslice.nim:197:0 > msplit ["string", "seq[MSlice]", '' (empty string), '' (empty string), '' (empty string)] > ["s", "fs", "sep", "n", "repeat"] :: cligen/mslice.nim:200:0 > ["s", "fs", "seps", "n", "repeat"] :: cligen/mslice.nim:208:0 > splitr ["string", "seq[string]", '' (empty string), '' (empty string), '' (empty string), "seq[string]"] > ["s", "fs", "sep", "n", "repeat", "sp"] :: cligen/mslice.nim:255:0 > ["s", "fs", "seps", "n", "repeat", "sp"] :: cligen/mslice.nim:264:0 ## cligen > [] ["Tern[T]", "string"] > ["t", "key"] :: cligen/tern.nim:130:0 > ["c", "key"] :: cligen/tern.nim:134:0 ## collections > items ["Iterator[T]"] > ["i"] :: collections/iterate.nim:51:0 > ["s"] :: collections/iterate.nim:60:0 ## qex > inlineLets ["NimNode"] > ["n"] :: src/base/metaUtils.nim:196:0 > ["x"] :: src/base/metaUtils.nim:1387:0 ## libp2p > write ["VBuffer", "PrivateKey"] > ["vb", "seckey"] :: libp2p/crypto/crypto.nim:994:0 > ["vb", "sig"] :: libp2p/crypto/crypto.nim:999:0 ## libp2p > getValue ["ProtoBuffer", "ProtoHeader", "T"] > ["data", "header", "outval"] :: libp2p/protobuf/minprotobuf.nim:424:0 > ["data", "header", "outBytes"] :: libp2p/protobuf/minprotobuf.nim:494:0 ## binance > aggrTrades ["BinanceApi", "string", "int32", "int32", "int16"] > ["b", "symbol", "fromId", "startTime", "limit"] :: src/binance/rest_api.nim:130:0 > ["b", "symbol", "fromId", "endTime", "limit"] :: src/binance/rest_api.nim:138:0 > aggrTrades ["BinanceApi", "string", "int32", "int16"] > ["b", "symbol", "startTime", "limit"] :: src/binance/rest_api.nim:146:0 > ["b", "symbol", "endTime", "limit"] :: src/binance/rest_api.nim:153:0 > ["b", "symbol", "fromId", "limit"] :: src/binance/rest_api.nim:173:0 > candlesticks ["BinanceApi", "string", "CandlestickInterval", "int16", "int32"] > ["b", "symbol", "interval", "limit", "startTime"] :: src/binance/rest_api.nim:192:0 > ["b", "symbol", "interval", "limit", "endTime"] :: src/binance/rest_api.nim:198:0 > newOrder ["BinanceApi", "string", "OrderSide", "OrderType", "float", "float", "ResponseType", "int32"] > ["b", "symbol", "side", "orderType", "quantity", "stopPrice", "newOrderRespType", "recvWindow"] :: src/binance/rest_api.nim:322:0 > ["b", "symbol", "side", "orderType", "quantity", "price", "newOrderRespType", "recvWindow"] :: src/binance/rest_api.nim:394:0 > testNewOrder ["BinanceApi", "string", "OrderSide", "OrderType", "float", "float", "ResponseType", "int32"] > ["b", "symbol", "side", "orderType", "quantity", "stopPrice", "newOrderRespType", "recvWindow"] :: src/binance/rest_api.nim:495:0 > ["b", "symbol", "side", "orderType", "quantity", "price", "newOrderRespType", "recvWindow"] :: src/binance/rest_api.nim:567:0 ## godot > keys ["Dictionary"] > ["self"] :: godot/core/dictionaries.nim:57:0 > ["dict"] :: godot/core/dictionaries.nim:87:0 > values ["Dictionary"] > ["self"] :: godot/core/dictionaries.nim:60:0 > ["dict"] :: godot/core/dictionaries.nim:92:0 ## godot > toVariant ["T"] > ["val"] :: godot/nim/godotnim.nim:598:0 > ["self"] :: godot/nim/godotnim.nim:639:0 > ["t"] :: godot/nim/godotnim.nim:826:0 > fromVariant ["T", "Variant"] > ["self", "val"] :: godot/nim/godotnim.nim:512:0 > ["self", "val"] :: godot/nim/godotnim.nim:607:0 > ["self", "val"] :: godot/nim/godotnim.nim:642:0 > ["self", "val"] :: godot/nim/godotnim.nim:649:0 > ["self", "val"] :: godot/nim/godotnim.nim:657:0 > ["s", "val"] :: godot/nim/godotnim.nim:784:0 > ["t", "val"] :: godot/nim/godotnim.nim:836:0 ## syndicate > enqueueScriptAction ['' (empty string), "Action"] > ["actor", "action"] :: src/syndicate/dataspaces.nim:227:0 > ["facet", "action"] :: src/syndicate/dataspaces.nim:230:0 ## redux > $ ['' (empty string)] > ["filter"] :: examples/todos.nim:36:0 > ["state"] :: examples/todos.nim:42:0 ## gtk2 > right_justify ["PMenuItem"] > ["a"] :: src/gtk2.nim:12265:0 > ["menu_item"] :: src/gtk2.nim:12292:0 ## nimnoise > setXScale ["TranslatePoint", "float64"] > ["tp", "xTranslation"] :: src/nimnoise.nim:809:0 > ["tp", "yTranslation"] :: src/nimnoise.nim:810:0 > ["tp", "zTranslation"] :: src/nimnoise.nim:811:0 > setXScale ["ScalePoint", "float64"] > ["sp", "xScale"] :: src/nimnoise.nim:1127:0 > ["sp", "yScale"] :: src/nimnoise.nim:1128:0 > ["sp", "zScale"] :: src/nimnoise.nim:1129:0 ## nimnoise > setXScale ["TranslatePoint", "float64"] > ["tp", "xTranslation"] :: src/nimnoise/translatepoint.nim:23:0 > ["tp", "yTranslation"] :: src/nimnoise/translatepoint.nim:24:0 > ["tp", "zTranslation"] :: src/nimnoise/translatepoint.nim:25:0 ## nimnoise > setXScale ["ScalePoint", "float64"] > ["sp", "xScale"] :: src/nimnoise/scalepoint.nim:23:0 > ["sp", "yScale"] :: src/nimnoise/scalepoint.nim:24:0 > ["sp", "zScale"] :: src/nimnoise/scalepoint.nim:25:0 ## nim-parsec > runParser ["Parser", "string"] > ["p", "s"] :: src/nim_parsec.nim:47:0 > ["p", "inp"] :: src/nim_parsec.nim:65:0 ## bingo > storeBin ["Stream", "T"] > ["s", "x"] :: bingo.nim:9:0 > ["s", "x"] :: bingo.nim:11:0 > ["s", "o"] :: bingo.nim:46:0 > ["s", "o"] :: bingo.nim:58:0 > ["s", "o"] :: bingo.nim:65:0 ```

If anything this is an anti-feature, which allows for very unexpected behavior and cryptic errors in forward declarations - forward(a: int) cannot be used to declared forward(b: int). So I suggest it to be removed/deprecated entirely as it does not provide any substantial value.

solo989 commented 3 years ago

Ambiguous call errors are very easy to resolve and in the absence of name only arguments I think it's a decent substitute. Forward declarations should match their signature exactly anyway. The compiler already complains when you don't export the forward declaration but do export the main declaration.

I've experimentally done some converting of procs default arguments to constants and removing this feature would break that kind of code.

#b and c are meant to called as named only arguments in this context
proc hi(a : int, b : int = 5, c : int = 6) =
  when b is static:
    discard
  else:
    discard

#macro to create this
proc hi(a : int, b : int, c : int) =
  discard
proc hi(a : int, b : int) =
  const c = 6
proc hi(a : int, c : int) =
  const b = 5
proc hi(a : int) =
  const 
    b = 5
    c = 6

Regardless ambiguous call errors are so easy to fix that removing this feature doesn't seem worth it.

juancarlospaco commented 3 years ago

Is useful for DSL ?, kinda like:

proc render(img:   string) = imgImpl(img)
proc render(video: string) = videoImpl(video)

render img="kitten.png"       # <img src="kitten.png"></img>
render video="bongocat.webm"  # <video src="bongocat.webm"></video>

Too simple example (not really a DSL) but you get the idea anyways... 🤔

haxscramper commented 3 years ago

@solo989 you seem to be overly focused on my latter note related to the ambiguous call errors - it is not the main point I'm trying to make, I just provided an example where this feature is somewhat undesired. My main argument is - if it is almost never used, why keep this feature anyway?

Also, I'm having a hard time figuring out a real-world use case for your code example - can you please elaborate, what kind of problem does it solve?


@juancarlospaco

Is useful for DSL ?, kinda like:

Sure, according to Hyrum's Law there might actually be someone who even built DSL that functions this way, but I would choose renderImg or renderVideo every single time. I mean, if you already have to write render img already, why does it have to be a separate obscure feature? It is also not clear from the procedure name alone. In addition, render img = "kitten.png" does not compile, as render img = 12 is parsed as

  Asgn
    Command
      Ident "render"
      Ident "img"
    IntLit 12

So you actually have to do this, which is a clear step back from renderVideo:

proc render(img:   string): string = "<img src=\"" & img & "\"></img>"
proc render(video: string): string = "<video src=\"" & video & "\"></video>"

# /home/jail/prog.nim(5, 8) Error: undeclared identifier: 'img'
echo render(img="kitten.png"       )
echo render(video="bongocat.webm"  )
solo989 commented 3 years ago

My point is if the problems with it are trivial to deal with and the maintenance cost is nil then there is no point in removing this feature.

I spend very little time dealing with ambiguous errors. Most of my time is spent debugging complete type mismatches or trying to decipher error messages generated by templates or macros calling each other.

Also with juans example you could generate code similar to this.

template r(p,img,video) : untyped =
  echo p(img=img)
  echo p(video=video)
  echo p(somethingElse1="someValue1")
  echo p(somethingElse2="someValue2")
  echo p(somethingElse3="someValue3")
  #add a dozen other procs with the same name here
r(render,img,video)

as Opposed to

template r(p1,p2,p3,p4,p5,etc,img,video) : untyped =
  echo p1(img=img)
  echo p2(video=video)
  echo p3(somethingElse1="someValue1")
  echo p4(somethingElse2="someValue2")
  echo p5(somethingElse3="someValue3")
  #add a dozen other procs here
r(render1,render2,render3,render4,render5,img,video)

Separating it into multiple procs with seperate names would make it harder to deal with.

juancarlospaco commented 3 years ago

Sure, according to Hyrum's Law there might actually be someone who even built DSL that functions this way

Ok, I upvoted it, kill it with fire. 🙂:+1:

haxscramper commented 3 years ago

@solo989 again - this is not a question whether the feature can be used somehow. But I feel there should be a justification in the form of some real-world use (not some abstract never-before-seen issue), where problem cannot be solved (easily) using different functionality, otherwise it makes sense to trim down on unused features, especially when they are obscure, never used and can be easily replicated using already existing features:

# Actually clear what procedure does based on it's name
proc renderImg(img:   string): string = "<img src=\"" & img & "\"></img>"
proc renderVideo(video: string): string = "<video src=\"" & video & "\"></video>"

# render ?? image or video, and you need to remember to call correct `type = ` 
# also does not work as good with autocomplete.
proc render(img:   string): string = "<img src=\"" & img & "\"></img>"
proc render(video: string): string = "<video src=\"" & video & "\"></video>"

template r1(p, inImg, inVideo) : untyped =
  echo p(img = inImg)
  echo p(video = inVideo)

template r2(p, inImg, inVideo) : untyped =
  # Joining identifier is a more general feature
  echo `p Img`(inImg)
  echo `p Video`(inVideo)

# Works the same way
r1(render,"img","video")
r2(render,"img","video")
solo989 commented 3 years ago

Ok heres an example. I ended up making by own version of walk with a bunch of possibly static arguments. The way I made it was rather clunky and full of whens and some macro nonsense. If I were to recreate it I would probably do something like this

my code actually had a bunch more optional args then this so I was always passing them as named arguments anyway also command syntax doesn't look that good when the argument lists get long

template ifWhen(a : bool, b : untyped) : untyped =
  if a:
    b

template ifWhen(a : static[bool], b : untyped) : untyped =
  when a:
    b

iterator walk(files : string, slowCheck = staticCheck, slowCheck2 = staticCheck) : string {.splitInto4Functions.} =                                                                                                                                 
  for file in files:

    ifWhen slowCheck:
      ifWhen slowCheck2:
        discard
      else:
        discard
    else:
      discard
    body

iterator walk(files : string, slowCheck = staticCheck, slowCheck2 = staticCheck) : string =
  ifWhen slowCheck:
    fastCheck = true
    ifWhen slowCheck2:
      fastCheck2 = true
    else:
      fastCheck2 = false
  else:
    fastCheck = false
  for file in files:
    if fastCheck:
      discard
    etc
    body
for file in walk(dir):
  discard
for file in walk(dir,slowCheck=slowCheckProc):
  discard

now you could move some of those checks outside of the loops but then you have to reconfigure your code

you could even drop the ifwhens as the ifs will probably be turned into if true or if false anyway

I have no idea if nim optimizes efficiently for static default arguments when they are passed in normally.

Also turning generics into non generics makes symbol binding more predictable.

Frankly removing features that have no maintenance cost and are potentially useful for creating more efficient generic code seems wasteful.

Also a lot of those obscure never before seen issues are only obscure until an algorithm is implemented that makes it obvious that this is how you should have solved the problem in the first place.

I think we are still in early days of figuring out how we can help the computer generate much more efficient code with less and less intervention from the programmer.

haxscramper commented 3 years ago

Well, it seems like there is a conflict of two points of view - "0.04% of use cases is not enough to justify feature existence" vs "if it does not trouble anyone, why remove it?". I think we need someone who can say whether it is really zero-maintenance or not. If it is, then I guess the issue could be closed and this feature could continue with it's "out of sight, out of mind" status.


On a side note - I still don't understand your example, or how named parameter overloading helps it. You want to have a parameter (check? I would assume it is a callback in the form of proc(item: file): bool) and you want to save on execution time by splitting procedure declaration into 2^(number-of-args) overloads that user would have to select based on the argument names, where implementation would check whether an argument was actually static, and act at compile-time based on that?

iterator walk(files: string, slowCheck = staticCheck, slowCheck2 = staticCheck): string =                                                                                                                                 
  for file in files:
    ifWhen slowCheck: # Avoid calling preciate if it is a static value to save on execution time?

      ifWhen slowCheck2:
        discard
      else:
        discard
    else:
      discard

# ... two more overloads with `slowCheck` and `slowCheck2` parameters

iterator walk(files: string): string = 
  const
    slowCheck = staticCheck
    slowCheck2 = staticCheck  

  for file in files:
    ifWhen slowCheck:
      ifWhen slowCheck2:
        discard
      else:
        discard
    else:
      discard

I have no idea if nim optimizes efficiently for static default arguments when they are passed in normally.

import std/macros

proc arg(a: static[int] = 12) = 
  expandMacros:
    echo a

arg(12)
echo ["12"]
12

If you mean static[T] arguments - they behave the same way as const, so constant folding and other optimizations apply.

solo989 commented 3 years ago

I should've said default arguments that are normally non static that have static values as their defaults. I'm not sure nim optimizes for such a case since it would require creating two different versions of the proc.

Actually most defaults on most functions are static.

I was thinking each function would have two versions of a parameter. A const value default or a runtime value.

You could even add another variation and add a third static value that the user can pass in.

Araq commented 3 years ago

The feature is a natural outcome of the used algorithm and hypothetical alternative implementations would have the same behavior or at least can easily ensure to support the feature with minimal cost. Disallowing this form of overloading is more costly as it requries special logic in a compiler.

haxscramper commented 3 years ago

Alright, if keeping it is cheaper than removing lets leave it alone. Not really useful or used, but it does not really matter.

metagn commented 2 years ago

Sorry to comment on a closed issue like this but IMO the best style to write this:

proc render(img:   string) = imgImpl(img)
proc render(video: string) = videoImpl(video)

render img="kitten.png"       # <img src="kitten.png"></img>
render video="bongocat.webm"  # <video src="bongocat.webm"></video>

Would be this:

type Image = distinct string
type Video = distinct string

proc render(img:   Image) = imgImpl(img)
proc render(video: Video) = videoImpl(video)

render Image("image.png")
render Video("video.webm")