Your challenge is to write a polyglot that works in different versions of your language. When run, it will always output the language version.


  • Your program should work in at least two versions of your language.
  • Your program's output should only be the version number. No extraneous data.
  • Your program may use whatever method you like to determine the version number. However, the output must follow rule 2; however you determine the version number, the output must only be the number.
  • Your program only needs to output the major version of the language. For example, in FooBar 12.3.456789-beta, your program would only need to output 12.
  • If your language puts words or symbols before or after the version number, you do not need to output those, and only the number. For example, in C89, your program only needs to print 89, and in C++0x, your program only needs to print 0.
  • If you choose to print the full name or minor version numbers, e.g. C89 as opposed to C99, it must only print the name. C89 build 32 is valid, while error in C89 build 32: foo bar is not.
  • Your program may not use a builtin, macro, or custom compiler flags to determine the language version.


Your score will be the code length divided by the number of versions it works in. Lowest score wins, good luck!

  • 4
    \$\begingroup\$ What is a language version number? Who determines it? \$\endgroup\$ – Post Rock Garf Hunter Aug 16 '17 at 4:34
  • 9
    \$\begingroup\$ I think that inverse-linear in the number of version does not welcome answers with high number of versions. \$\endgroup\$ – user202729 Aug 16 '17 at 5:23
  • 6
    \$\begingroup\$ @user202729 I agree. Versatile Integer Printer did it well - score was (number of languages)^3 / (byte count). \$\endgroup\$ – Mego Aug 16 '17 at 5:37
  • 6
    \$\begingroup\$ What is the version for a language? Isn't we define a language as its interpreters / compilers here? Let we say, there is a version of gcc which has a bug that with certain C89 codes it produce an executable which behavior violate the C89 specification, and it was fixed on the next version of gcc. Should this count a valid solution if we write a piece of code base on this bug behavior to tell which gcc version is using? It targeting on different version of compiler, but NOT different version of language. \$\endgroup\$ – tsh Aug 16 '17 at 7:21
  • 6
    \$\begingroup\$ I don't get this. First you say "Your program's output should only be the version number.". Then you say "If you choose to print the full name or minor version numbers, e.g. C89 as opposed to C99, it must only print the name." So the first rule is not actually a requirement? \$\endgroup\$ – pipe Aug 16 '17 at 9:13

51 Answers 51

1 2

PHP 4, 5, & 7: 59 bytes, score 19.67


It's way long but it does 3 versions!

list() assigns in backwards order in PHP 4 & 5, strrpos() only searches for a single character in PHP 4.


C++11 and C++14, 96 bytes, score 48

#define M(x,...)__VA_ARGS__
int main(){std::cout<<(int[]){M(1'2,1'4,11)}[0];}

The code is based on the digit separator proposal which has very similar example code at the end.

online version


Braingolf, 4 bytes, 2 versions, score = 2


Try it online!

n (negate) was added in braingolf v0.7, meaning this will output 7 in v0.7 and newer, and 6 in v0.6 and older

In v0.7, n "negates" the 0 to a 1, then adds it to 6 to make 7, but in v0.6, the n does nothing, and so 0 is added to 6, resulting in 6. Both versions have implicit output.

This is technically the minor version, however there is only 1 major version of Braingolf, as Braingolf v1.0 isn't a thing yet, so this is about as major as the version numbers get.


APL (Dyalog), score 7.3 or 3

51 bytes for 7 versions or 6 bytes for 2 versions

For the lowest score, distinguish versions 12 and 13 with:


Twelve plus the depth of the first zero integers. Until version 12, ⍳0 (wrongly) returned the index origin as a scalar, i.e. depth 0. In version 13 and up, ⍳0 returns an empty list, i.e. depth 1.

Much more interesting is the following anonymous function which distinguishes all major versions 10 through 16 (the current). It needs a dummy argument (which is ignored) to run.


{} an anonymous function which allows setting error guards :: with a numeric error code (determining which type of error to catch) on the left, and on the right, the result to be returned in case of error.

2::10 if SYNTAX ERROR, return 10

⌷2::11 materialise (introduced in version 11) 2 (SYNTAX ERROR) which if happens, yields 11

⋄_←⎕XML try: assign XML system function (introduced in version 12) to a dummy name

0:: if any error happens;

  ⍳0 first zero integers (gives 1 until version 12 and empty list from version 13)

   depth of that (0 until 12, 1 from 13)

  12⎕ML, prepend 12 and the Migration Level (0 until 13, 1 from 14)

  +/ sum (12 until 12, 13 in 13, 14 from 14)

≡,/⍬::15 try: concatenate the elements of an empty list (introduced in version 15) and get its depth, 2 (SYNTAX ERROR) which if happens, yields 15

⋄16@~0 try: amend with a 16 at (introduced in version 16) positions where logical NOT yields truth, applied to 0

Try it on TryAPL (slightly modified to pass security) and Try it Online!


Idea of the solution

Each version hava added a new syntax features, so code vith them will cause a syntax error in smaller versions of the language.

  • ES5: Octal constants 03 are disallowd in strict mode (unused in actual solutions)
  • ES6: Octal constants 0o6 are introduced
  • ES7: Pow operator is introduced: 7**1
  • ES8: Trailing comma in function call is allowed: f(8,)
  • ES9: Tagging of invalid strings is allowed: f`\u9`

So evaluating


on one of the examples will either return the the value, or throw a SyntaxError. But what value would it return? For all ES6-ES8 snippets the last calculated value will be the version. In ES9 snippet the 9 from 9; is return. So if we place a minimal supported version into catch block like


we'll get the behaviour we want.

Ecmascript 5+ (for 5, 6, 7, 8, 9); 124 / 5 = 24.8 points

alert(Math.max.apply(0,'0o6~7**1~f(8,)~f`\\u9`'.split('~').map(function f(s){return eval("try{eval('9;'+s)}catch(e){5}")})))

Tested in:

  • IE8: Error (Object doesn't support property or method 'map')
  • IE 11: 5
  • Edge 14: 8
  • Chrome 60: 8
  • Firefox 55: 9

Ecmascript 6+ (for 6, 7, 8, 9); 96 / 4 = 24 points


Tested in:

  • Edge 14: 8
  • Chrome 60: 8
  • Firefox 55: 9
  • \$\begingroup\$ Oh, now this is very nice :) I'm not sure about the inclusion of ES9, though, seeing as the official spec doesn't exist yet. I'd suggest including an explanation to help those unfamiliar with JS to understand what's going on here. Also, could you save any bytes using reduce instead of max? Oh, and this outputs 8 for both in Edge 14, if you want to include it. \$\endgroup\$ – Shaggy Aug 18 '17 at 12:49
  • \$\begingroup\$ @Shaggy, here is a single feature for ES 2018 which already got out of ES next and I used it. \$\endgroup\$ – Qwertiy Aug 18 '17 at 12:51
  • \$\begingroup\$ Ah, I wasn't aware that that had made it out. \$\endgroup\$ – Shaggy Aug 18 '17 at 13:02
  • 1
    \$\begingroup\$ @Shaggy, added a description. \$\endgroup\$ – Qwertiy Aug 18 '17 at 13:04

Lua 5.3.3 / 5.2.4, 32 bytes / 2 = 16 points


This actually abuses a bug in Lua 5.2's string.gsub function: In Lua 5.2, "a?$" would first match "a{endOfString}" and then "{endOfString}" a second time, which is obviously incorrect, because gsub may only match each character/anchor in the string once. This results in "a" being substituted into the string twice, giving a final string length of 2.

In Lua 5.3, the bug is fixed, so "a?$" only matches "a{endOfString}", thus matching the end of the string only once, which is the correct behavior. Therefore, "a" is substituted into the string just once, resulting in a string length of 1.

The difference between the string length and 54 then gives the major/minor version number of the Lua interpreter, which is printed as follows:

Lua 5.2.4: 52
Lua 5.3.3: 53

(I could only test on these two specific versions, but it should work on more)


Python 1, 2, and 3, 43 bytes, score 14.333

if str(1<2)=="1":print(1)

Yes, there are already submission for Python, but this one also differentiates Python 1 and 2.

The difference between Python 3 and 2 are pretty well known, but with Python 1? It's very similar to Python 2.

But I found something.

In Python 1, a true statement evaluates to 1. In Python 2 and 3, it evaluates to True. By turning a true statement into a string, you can differentiate Python 1 and the later versions.


VBA, 51 Bytes / 2 Codes = 25.5

Anonymous VBE immediate window function that outputs the VBA program version - works only in Excel 2011 and later. Outputs Version 7 for windows and Version 6 for Mac, as mac has yet to get version 7 of VBA

Uses conditional compilation constant Mac to determine the VBA language version

Sub a()
#If Mac Then
#End If
End Sub
  • \$\begingroup\$ @Shaggy, missed that, corrected \$\endgroup\$ – Taylor Scott Aug 16 '17 at 17:02

Lua 5.0, 5.1, 5.2, 5.3, score: 17.75

print("5."..(table.getn or load'return #...'){math.fmod,_ENV,math.ult})

math.fmod was math.mod before 5.1
_ENV was introduced in 5.2 for new environment system
math.ult was introduced in 5.3

There was a problem: lua 5.0 don't know # for tables, but lua >= 5.2 don't have function table.getn, so I keep # isolated in string for lua 5.0, and loads in instead of table.getn for lua >= 5.2.


K/Kona, 6 bytes, 2 versions - score 3


Version 3 defaults to long numeric types, whereas 2 defaults to integer numerics. We enlist (,) to get a positively typed number (atoms are negative), we get its type (@), and we add that to -4.


Julia 0.2-0.7, bytes = 59, versions = 6, score = 9.833


Turns out each version of julia has had a different number of abstract array subtypes. So we use the number of subtypes to index into an array that contains the version number. The current nightly (0.7), currently has 25, but it is the fallback case anyway.

Julia 0.1 also has a different number of AbstractArray subtypes I would guess. However, julia 0.1 does not have the subtypes function. So I can't trivially retrieve them.

A significant improvement over my previous answer.

(Thanks @one-minute-more for almost halving the bytecount)

  • \$\begingroup\$ Golfing this to 52 bytes, for a score of 8.7: [4,0,3,0,5,2,6,7][endof(subtypes(AbstractArray))-16]. \$\endgroup\$ – one-more-minute Aug 18 '17 at 17:08
  • \$\begingroup\$ @one-more-minute thanks. I do need to add some boilerplate, but that is still quiet a saving. \$\endgroup\$ – Lyndon White Aug 19 '17 at 3:29

RProgN 1/2, 4 bytes / 2 versions. Score = 2

1 1+

RProgN1 requires commands to be split into words, where as RProgN2 reads byte by byte by default. As such, RProgN1 can't do anything with 1+, so it just outputs 1, but RProgN2 can execute it, so it adds 1 to the 1, giving 2.

Try RProgN1

Try RProgn2


T-SQL, 41 bytes / 11 versions = 3.7 score

SELECT MAX(cmptlevel)/10FROM sysdatabases

Note that this does not use a SQL "builtin, macro, or custom compiler flag"; that would be something like SELECT @@VERSION or SELECT SERVERPROPERTY('productversion').

Instead, this looks up the highest "compatibility level" for all databases on the server. Existing databases on a server might have older compatibility levels, but the system database tempdb is recreated each time the server is restarted, so will always have the compatibility level of the actual current version. (Using MAX is shorter than including WHERE name='tempdb').

I divide by 10 so it returns the actual SQL version number.

I've successfully tested this on all versions between SQL 2005 and 2017, but I believe this will ultimately work in 11 distinct versions:

Version Number    Release Name
6                 SQL Server 6.0
6.5               SQL Server 6.5
7                 SQL Server 7.0
8                 SQL Server 2000
9                 SQL Server 2005
10                SQL Server 2008 (or SQL Server 2008 R2)
11                SQL Server 2012
12                SQL Server 2014
13                SQL Server 2016
14                SQL Server 2017
15                SQL Server 2019 (Pre-release)

Let me know if anyone has SQL 2000 or older still running to see if this works as expected.

Note that my code uses a deprecated system table for widest compatibility, I'd normally use the newer sys.databases system view introduced in SQL 2005.


Japt, 4 bytes / 2 versions = 2


Japt | Japt 2.0



Japt | Japt 2.0


Wolfram Language (Mathematica), 30 bytes (5<= Score <=10)


I tested this on Mathematica 10.2, which has 5683 named symbols and 11.3, which has 6280 names symbols. I used cloud.wolfram.com to test it on Mathematica 12.0 which has 6914 named symbols. With possibly some slight tweaking to the numbers, this could work for as many as 6 versions (see the graph at https://blog.wolfram.com/2018/06/21/weve-come-a-long-way-in-30-years-but-you-havent-seen-anything-yet/) which shows that the function count has grown pretty much linearly since v6.)

Try it online!


Whitespace, 43 bytes, 2 versions, score 21.5

Version 0.3 of Whitespace added the copy and slide instructions. Of those two, slide is exploited here. Slide removes the top n elements of the stack, keeping the top element. The code for version 0.3 slides 2 and version 0.2 ignores the slide and keeps the 2.


Written as more readable Whitespace assembly:

push 0   # Push 0 to the stack
printi   # Print 0 as an integer
push 3   # Push 3 to the stack
push 2   # Push 2 to the stack
push '.' # Push 46 ('.') to the stack
slide 1  # Pop 2 if version 0.3 or skip instruction
printc   # Print 46 as character '.'
printi   # Print 2 or 3 as integer

Julia: 21 bytes, score: 10.5

(length([1:2])+V)/10   # where V takes value critical version minus 1

I cannot quite remember which version of julia this changed in (stuff gets deprecated very often), but there was some point in the language where this:

julia> [1:2]
2-element Array{Int64,1}:

became this:

julia> [1:2]
1-element Array{UnitRange{Int64},1}:

from one version* to the next. I think it was probably from v0.3 to v0.4? (in which case V = 2)

* julia is still in major version 0


Mathematica 10/11 20 Bytes, Score=10

Since Echo was introduced in version 11


Can extend to version 9 as well (since AbsArg was intro'd in V10) with


but the score goes up to 34/3 or 11.33


SmileBASIC (2 and 3), 9 bytes

Thank you 12Me21 for helping out.


In SB, variable declaration rules are lazy by default; if a variable name is not declared or assigned anywhere in scope, it is assumed to be 0. There's also a handful of reserved system variables and constants which varies between version and platform.

In SB v2, CANCEL was a reserved variable that represented -1; this was intended for use with RESULT to check the state of a dialog box or other action. In SB v3, this variable is not present.

Thus, when this program is run on a standard SB v2 environment it prints 2, and on a v3 environment it prints 3.


05AB1E/05AB1E (legacy)/2sable, score 9 8⅔ (26 bytes with 3 versions)

•äƵí•hR®di’ÿ (»Š)’ë’2sˆ¢’r

Try it online in 2sable
Try it online in 05AB1E (legacy)
Try it online in 05AB1E


Let's start with a bit of history of these three versions. The development of 05AB1E started at the start of 2016 (or actually, the very first git-commit was on December 21st, 2015). This new codegolf language was being built in Python as backend. Mid 2016 2sable as branched of that current 05AB1E version (July 7th, 2016 to be exact), and the strength of 2sable in comparison to that old 05AB1E version was added back then: implicit inputs. Later on implicit input was also added to 05AB1E, and 2sable was basically a forgotten version right after it was created on that day July 7th, 2016.
Then in mid-2018, a new 05AB1E version was being started, this time completely rewritten in Elixir instead of Python, with loads of new builtins added and some builtins changed or even removed.

So, let's go over the code and see what it does in each of the three versions:

•äƵí•              # Legacy 05AB1E / new 05AB1E: push compressed integer 14793296
                   # 2sable: no-op (compressed integers didn't exist yet), 
                   #         so nothing is pushed to the stack
     h             # Convert this from an integer to a hexadecimal string: "E1BA50"
      R            # Reverse this string: "05AB1E"
                   # (2sable: the stack remains empty for `h` and `R`)
       ®           # Push -1
        d          # 2sable: check if -1 only consist of digits (falsey)
                   # 05AB1E (legacy): check if -1 is an integer (truthy)
                   # New 05AB1E: check if -1 is a non-negative integer ≥0 (falsey)
         i         # If it is truthy:
          ’ÿ (»Š)’ #  Push dictionary string "ÿ (legacy)",
                   #  where the `ÿ` is automatically filled with the top of the stack
         ë         # Else:
          ’2sˆ¢’   #  Push dictionary string "2sable"
                r  #  And reverse the stack
                   #  (05AB1E: stack changes from "05AB1E","2sable" to "2sable","05AB1E"
                   #   2sable: stack stays the same "2sable")
                   # (after which the top of the stack is output implicitly as result)

See this 05AB1E tip of mine (sections How to use the dictionary? and How to compress large integers?) to understand why ’ÿ (»Š)’ is "ÿ (legacy)", ’2sˆ¢’ is "2sable", and •äƵí• is 14793296.

  • \$\begingroup\$ Out of curiosity, is there a name for this sort of table/explanation method? Where the lines are ordered by English logic, but commands are padded to their place in the code. \$\endgroup\$ – Geza Kerecsenyi Nov 20 '19 at 20:34
  • \$\begingroup\$ @GezaKerecsenyi I to be honest have no idea. I kinda made it up for my answers tbh. I know some other people do their comments similarly, but no idea if it's a known convention and it has a name. \$\endgroup\$ – Kevin Cruijssen Nov 20 '19 at 21:29
  • \$\begingroup\$ OK, thanks anyway! \$\endgroup\$ – Geza Kerecsenyi Nov 20 '19 at 21:30

Red, 14 bytes all versions

  • 4
    \$\begingroup\$ Welcome to the site! I'm not familiar with Red, but does this violate the rule "Your program may not use a builtin, macro, or custom compiler flags to determine the language version."? \$\endgroup\$ – caird coinheringaahing Jan 25 at 20:50
1 2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.