4

The if expression is not working if used inside of fmt string.

Why, and how to make it work?

import strformat

let v = if true: 1 else: 2 # <= Works
echo fmt"{v}"

echo fmt"{if true: 1 else: 2}" # <= Error
Alex Craft
  • 13,598
  • 11
  • 69
  • 133

2 Answers2

8

why?

because fmt uses : to separate value of expression from format specifier so (see docs and implementation) the line

echo fmt"{if true: 1 else: 2}"

is expanded by the macro into

var temp = newStringOfCap(educatedCapGuess)
temp.formatValue if true, " 1 else: 2"
temp

which clearly does not compile.

how?

update

currently (April 2021) in devel branch there is a enhancement that allows to use any expression inside a formatted string. For the specific case mentioned you need to surround the expression by parenthesis:

echo fmt"{(if true: 1 else: 2)}"

the new enhancements also allow to use curly brackets in expression (escaping them).

See:

This enhancement will be released for the general public in the next stable version (likely to be 1.6).

old content

I guess it could be seen as a limitation of fmt and I do not think there is currently a way to use an expression with : in fmt where it does not act as a format specificier.

One way to fix this would be to provide an additional formatSpecifierSeparator keyword argument in order to change the default : and be able to do something like:

echo "{if true: 1 else: 2}".fmt('|')

Another way would be to change the implementation of strformatImpl and make sure that the part before a : actually compiles before interpreting : as a formatSpecifier separator.

Both of these ways imply a PR in nim-lang code and would be available after next release or on devel, if accepted and merged.

pietroppeter
  • 1,433
  • 13
  • 30
  • 1
    I upvoted your comment but, ¿do Nim really want this? Python had a moment with multiple string interpolation methods plus Templates, and it was one of the weirdest part of the language. And you cannot include complex code in a f-string or you get a SyntaxError. Nim has the strutils %, the strformat, the SCFilters, and a template/macro system that allows basically to do whatever you need. Do it need full compiler inside fmt""? – xbello Aug 28 '20 at 07:52
  • You raise very good points. I also agree with you that Python situation in that respect is not that nice (I did use https://pyformat.info/ a lot!). I agree that Nim does not *need* to support this type of syntax but it might *want* at some point. I find the example simple enough to be reasonable given what fmt can be expected to do. I do feel that a complete answer to the question should include some suggestion on how to make it work, if one actually really cares. And if somebody will care enough to open a PR in Nim we will probably also find out if Nim wants that! :) – pietroppeter Aug 28 '20 at 13:39
  • @xbello I never had slightest issue with having full support for string interpolation in TypeScript, Kotlin or Ruby. – Alex Craft Aug 29 '20 at 13:04
  • @xbello you may say that it would be complicated to implement in Nim and it's better to keep it simple - and that is ok and a valid argument. But saying that string interpolation in general has some inherent problems is not a valid objection. – Alex Craft Aug 29 '20 at 13:11
  • came up again today in nim chat and there was discussion of possible workarounds (implement a ternay operator without : or use elvis) and of alternative implementation changes (using parenthesis to protect from parsing formatSpecifier): https://irclogs.nim-lang.org/04-02-2021.html#09:00:28 – pietroppeter Feb 04 '21 at 09:24
5

this works:

import std/strformat
let x = 3.14
assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847"
assert fmt"{(if true: 1 else: 2)}" == "1"
assert fmt"{if true\: 1 else\: 2}" == "1"

and avoids conflicts with format specifier. See https://github.com/nim-lang/Nim/pull/17700 for more details.

timotheecour
  • 3,104
  • 3
  • 26
  • 29