I am obviously missing something here regarding optional defaulted parameters using templates:
# Defaulting is supported in the following (even when there is an untyped parameter)
var lastGreeting: string
template greet*(lastGreeting: untyped, text = "Hello", who = "Bob") =
lastGreeting = text & " " & who
echo lastGreeting
greet lastGreeting
greet(lastGreeting, "Hi")
greet(lastGreeting, "Salute", "Fred")
greet(lastGreeting, who = "Tom")
echo "\nLastGreeting: ", lastGreeting
# Generates the expected output:
# Hello Bob
# Hi Bob
# Salute Fred
# Hello Tom
#
# LastGreeting: Hello Tom
echo "---"
# Now for my *failing* example template with optional parameters
let Chrome = "Chrome"
template webSession*(
sessionInfo: untyped,
baseURL: string,
browser = Chrome,
headless = true,
actions: untyped
) =
# Mock open session
var sessionInfo =
"open Session(" &
"baseURL=" & "\"" & baseURL & "\", " &
"browser=" & $browser & ", " &
"headless=" & $headless & ")"
actions
# Mock session cleanup
echo "close Session"
# The following works fine and outputs:
# open Session(baseURL="https://mysite.com", browser=Chrome, headless=false)
# close Session
webSession(mySite, "https://mysite.com", Chrome, false):
echo mySite
# But actually trying to use default parameters:
# webSession(mySite, "https://mysite.com"):
# echo mySite
# Fails to compile with the following error:
#
# Expression: webSession(mySite, "https://mysite.com") do:
# echo [mySite]
# [1] mySite: string
# [2] "https://mysite.com": string
# [3]
# echo [mySite]: void
#
# Expected one of (first mismatch at [position]):
# [3] template webSession(sessionInfo: untyped; baseURL: string; browser = Chrome;
# headless = true; actions: untyped): untyped
I can't see what is different from the top example and the failing one. What silly thing am I missing :-)
make overloading
template webSession(sessionInfo: untyped, baseUrl: string, actions: untyped): untyped =
webSession(sessionInfo, baseUrl, Chrome, false, actions)
I already tried that also thinking that it would work... but adding
...
template webSession*(
sessionInfo: untyped,
baseUrl: string,
actions: untyped
) =
webSession(sessionInfo, baseUrl, Chrome, false, actions)
# But actually trying to use default parameters:
webSession(mySite, "https://mysite.com"):
echo mySite
generated the following compile time error:
/Users/dennismisener/work/Nim/test_template_optional_params.nim(64, 8) Error: undeclared identifier: 'mySite'
candidates (edit distance, scope distance); see '--spellSuggest':
(3, 3): 'system'
(3, 4): 'byte'
(3, 4): 'csize'
So it is the last untyped actions parameter which is causing the problem?
Changing the top example:
var lastGreeting: string
template greet*(lastGreeting: untyped, text = "Hello", who = "Bob",
actions: untyped) =
lastGreeting = text & " " & who
actions
echo lastGreeting
greet lastGreeting:
echo "Example 1"
greet(lastGreeting, "Hi"):
echo "Example 2"
greet(lastGreeting, "Salute", "Fred"):
echo "Example 3"
greet(lastGreeting, who = "Tom"):
echo "Example 4"
echo "\nLastGreeting: ", lastGreeting
does generate the same type of compile errors:
/Users/dennismisener/work/Nim/test_template_optional_params.nim(64, 8) Error: undeclared identifier: 'mySite'
candidates (edit distance, scope distance); see '--spellSuggest':
(3, 3): 'system'
(3, 4): 'byte'
(3, 4): 'csize'
dennismisener@Denniss-iMac nim % n test_template_optional_params.nim
/Users/dennismisener/work/Nim/test_template_optional_params.nim(64, 8) Error: undeclared identifier: 'mySite'
candidates (edit distance, scope distance); see '--spellSuggest':
(3, 3): 'system'
(3, 4): 'byte'
(3, 4): 'csize'
dennismisener@Denniss-iMac nim % n test_template_optional_params.nim
/Users/dennismisener/work/Nim/test_template_optional_params.nim(64, 8) Error: undeclared identifier: 'mySite'
candidates (edit distance, scope distance); see '--spellSuggest':
(3, 3): 'system'
(3, 4): 'byte'
(3, 4): 'csize'
dennismisener@Denniss-iMac nim % n test_template_optional_params.nim
/Users/dennismisener/work/Nim/test_template_optional_params.nim(10, 1) Error: type mismatch
Expression: greet lastGreeting do:
echo ["Example 1"]
[1] lastGreeting: string
[2]
echo ["Example 1"]: void
Expected one of (first mismatch at [position]):
[2] template greet(lastGreeting: untyped; text = "Hello"; who = "Bob";
actions: untyped)
Defaulting for the previous template parameters doesn't happen!!
If so, how can I achieve the desired effect of template parameter defaulting in such a case?
(or is this a sad case of you can't get there from here :-( )
One way to solve it (might be the only way, because I guess expansion of body after : to last argument relies on keeping order of arguments and skipping arguments messes this up).
You can use named arguments instead:
var lastGreeting: string
template greet*(lastGreeting: untyped, text = "Hello", who = "Bob",
actions: untyped) =
lastGreeting = text & " " & who
actions
echo lastGreeting
greet(lastGreeting, actions =
echo "Example 1")
greet(lastGreeting, "Hi", actions =
echo "Example 2")
greet(lastGreeting, "Salute", "Fred", actions =
echo "Example 3")
greet(lastGreeting, who = "Tom", actions =
echo "Example 4")
echo "\nLastGreeting: ", lastGreeting
Excellent suggestion @janAKali... that indeed does the trick (and without much extra cruft).
I only wished I had thought of it :-)
You could of course write a macro like this which simply makes the last argument explicitly be the actions argument:
let Chrome = "Chrome"
template webSessionImpl*(
sessionInfo: untyped,
baseURL: string,
browser = Chrome,
headless = true,
actions: untyped
) =
block:
# Mock open session
var sessionInfo =
"open Session(" &
"baseURL=" & "\"" & baseURL & "\", " &
"browser=" & $browser & ", " &
"headless=" & $headless & ")"
actions
# Mock session cleanup
echo "close Session"
import macros
macro webSession(nodes: varargs[untyped]): untyped =
result = newCall("webSessionImpl")
for i, node in nodes:
if i != nodes.len - 1:
result.add node
else:
result.add nnkExprEqExpr.newTree(newIdentNode("actions"), node)
webSession(mySite, "https://mysite.com", Chrome, false):
echo mySite
webSession(mySite, "https://mysite.com"):
echo mySite
webSession(mySite, "https://mysite.com", headless = false):
echo mySite
webSession(mySite, "https://mysite.com", browser = "Firefox"):
echo mySite
Note that I also wrapped your template in a block to avoid redefinition of mySite but I'm not sure you'd actually want that.
Thanks ... Very nice improvement.
Using a macro to work around trailing block parameter messing with other template parameter defaulting is clever. Since template parameter defaulting works fine when no trailing block parameter is specified... effectively hiding that parameter from the compiler solves the problem. I would have never come up with the myself.
I appreciate any simple but useful macros that I can learn from.