Differences from Pip Classic

The 2018-10-25 commit of Pip is known as Pip Classic. This is the version of Pip available at Try It Online! If you’re trying to run a Pip program on TIO and something isn’t working the way it should, you might be using a language feature that isn’t in Pip Classic. Here is a catalog of the differences between Pip 1.0 and Pip Classic, plus some tips on how to work around them. (For features added since Pip 1.0, see the What’s New page.)

Note: “lhs” = left-hand side argument of an operator and “rhs” = right-hand side argument (including the single argument of a unary operator).

Flags

  • The -x flag is not supported. Evaluate the arguments explicitly: use Va for a, V*g for g, etc.

Syntax

  • The Else branch of an If statement begins with E, not EL.
  • For loops do not support destructuring assignment. Switching to a functional programming approach will usually be shorter anyway: F[ab]lPaRb'_ -> P{aRb'_}MUl with the appropriate list-formatting flag.
  • The Swap and Unify statements exist.
  • Arbitrary-length variable names starting with $ are not supported.
  • There is no block comment syntax.

Data types

  • The str/repr of a Block is a stringified Python list representing a parse tree (with the outer [] delimiters changed to {}) rather than a string that evaluates to the same Block. Don’t use the str or repr of a Block for anything except debugging purposes.

Missing operators

  • Unary %. Mod by 2 explicitly: %a -> a%2.
  • Unary **. Raise 2 to the power explicitly: **a -> 2**a.
  • :: (binary). Use the swap command instead: a::b -> Sab.
  • Unary <>. Group into size-2 chunks explicitly: <>a -> a<>2.
  • \? (ternary), \| and \& (binary), and \! (unary). Use a curly-brace function instead of a lambda, or look for another way to express the formula: _\|'x -> {a|'x}; \!_ -> _=0 if the argument is guaranteed to be a number.
  • AD (binary). Subtract and take the absolute value instead: aADb -> AB(a-b).
  • D (unary). Use -- instead, but note the slight difference in precedence: Da -> --a; Da@<2 -> --(a@<2).
  • DB (unary). Multiply by 2 explicitly: DBa -> a*2.
  • E (unary and binary). Use ** instead: Ea -> 2**a; aEb -> a**b.
  • EE (unary and binary). Use the full formula instead: EEa -> 10**a; aEEb -> a*10**b.
  • FD (unary and binary). Use FB or roll your own base-conversion: aFDb -> $+a*b**RV,#a, or (Ja)FBb if b is 36 or less and all digits are less than 10.
  • Unary FI. Filter by the identity function instead: FIa -> _FIa.
  • H (unary and binary). Use @< instead, but note the difference in precedence: Ha -> @<a; aHb -> a@<b; a.bHc-2 -> (a.b)@<(c-2).
  • HV (unary). Divide by 2 explicitly: HVa -> a//2, or a/2 if a is known to be an even number.
  • OG (unary and binary). Use one of ZG, MC, or CG instead: OGa -> 1+ZGa or 1MCa; aOGb -> 1+(aZGb) or 1MMaCGb.
  • RE (unary). Use an explicit function call instead: REa -> (fa).
  • Unary R. Use RV instead: Ra -> RVa.
  • S (unary and binary). Use @> instead (with a negated second argument if binary), but note the difference in precedence: Sa -> @>a; aSb -> a@>-b; a.bSc-2 -> (a.b)@>(2-c).
  • SQ (unary). Multiply the number by itself or use ** instead: SQa -> a*a or a**2.
  • TD (unary and binary). Use TB or roll your own base-conversion: aTDb -> ^aTBb if b is 10 or less.
  • U (unary). Use ++ instead, but note the slight difference in precedence: Ua -> ++a; Ua@<2 -> ++(a@<2).

Operators with different behavior

  • Unary #, A, AB, C, FB, and SG have higher precedence than binary @, @>, and @<. Use parentheses or another precedence-manipulation strategy as needed: A*s@<3 -> A*(s@<3) or A*Ys@<3; #l@1 -> #(l1)
  • : does not support destructuring assignment. For simple cases, use the U statement: [xyz]:a -> UxyzWa. More-complex cases may need to use a series of assignments and/or unifications: C:[xyz] -> C:xC:yC:z; [[wx][yz]]:a -> UwxW@aUyzWa@1.
  • Numeric and string equality operators (=, <=, >=, Q, LE, and GE) always return falsey when their arguments are not comparable, rather than returning truthy if the arguments are identical. (In particular, nil is not equal to nil, and a Pattern is not equal to itself under numeric comparison.) If this becomes a problem, you can explicitly check for exact equality first: a<=b -> a==b|a<=b.
  • <> does not work with a negative rhs. To group from right to left, use RV twice: a<>-4 -> RV*(RVa<>4).
  • <> does not accept a List or Range as rhs. To return a list of different groupings, map a lambda function: a<>[2 4] -> a<>_M[2 4].
  • MC does not accept a height/width List as rhs. Use binary CG with MM instead, but note that MM does not unpack the coordinate pairs: _.s.BMC[ab] -> _@0.s._@1MMaCGb.
  • N and NI do not accept a List/Range lhs with a Scalar rhs. Map a lambda function instead: [1 2 4]Na -> _NaM[1 2 4].

Missing variables

(These still act as variables, but their initial values are nil rather than the useful values they have in current Pip.)

  • p: use "()" instead.
  • G: use {g} instead, or rework your lambda function using _ and B if possible: 2*$+G -> {2*$+g} or 2*$+{g} or, if you know there will always be two arguments, 2*_+2*B.
  • VD and VN: not supported, but not particularly useful for golf anyway.

Bugs

  • Using <> with a rhs of 0 or a negative number results in an infinite loop. Only use <> with positive rhs. See above for how to group from right to left.
  • A Scalar with leading whitespace in a numeric context evaluates to 0, rather than ignoring the whitespace. If leading whitespace is a possibility, strip it explicitly using |>.
  • A non-integer rhs to \, crashes the interpreter. Only use \, with integer rhs. If a non-integer rhs is a possibility, int-divide it by 1 to truncate it to an integer.
  • Using A with a Scalar rhs matching the regex 0+(\.0+)? results in nil rather than 48. Concatenating space or some other character to the end of the Scalar is one possible workaround.
  • < returns 1 when given two Lists that are equal. Swap the arguments and use > instead.
  • # and the length-comparison operators crash the interpreter when given infinite Ranges. Try to avoid taking the length of infinite Ranges. If you need to test the upper bound of a Range, use MX.
  • Iterating over an infinite Range with a lower bound of nil crashes the interpreter. Use a lower bound of 0 instead.
  • Indexing into an empty iterable and then attempting to assign a value to that expression crashes the interpreter. Make sure iterables are nonempty before attempting to assign to their items.
  • Indexing into a variable whose value is a Range gives nil. Convert to a List first (e.g. by multiplying by 1) or use math instead of indexing into a Range.
  • Applying an operator with the * meta-operator to a Block crashes the interpreter. Use a curly-brace delimited function instead of a lambda function, or find a way not to use the meta-operator: A*_ -> {A*a} or possibly A^_.
  • Weaving a List containing nil crashes the interpreter. If this ever becomes an issue, one potential workaround is to filter the List before weaving it: WVl -> WV(#_FIl).

Copyright © 2015-2024 David Loscutoff. Distributed on Github.