iogiiDocsSourceQuick RefOnline InterpreterContact
IOSyntaxTypesVectorizationCircular Programming ⸱ Ops ⸱ ExamplesWhy

Ops

One feature of iogii is that most ops are completely trivial and don't have any special cases. You can know everything there is to know just by looking at their name, type, and example. Their power comes from combining them.

This is a catalog of all ops that aren't obvious from their quickref description.

FYI Many ops are named after their Haskell equivalent.

Reshape

Take n elements from a list, then recurse on that many elements dropped. n will typically be the same number and hence the second arg is repeated instead of promoted so that you can provide a list instead (see Types:special reptitions).

Typical usage:

"abcdef" 2 reshape → ["ab","cd","ef"]
↗️

But you can take different numbers of elements at each step:

"abcdef" 1,2,3 reshape → ["a","bc","def"]
↗️

You can even take 0 elements (negative is treated as 0 also).

"abcdef" 3,0,3 reshape → ["abc","","def"]
↗️

If it ever tries to take more than there is, then it will terminate, returning what it found so far.

"abcdef" 4 reshape → ["abcd","ef"]
↗️

Trailing 0s will work if it didn't try to previously take more than there was:

"abcdef" 6,0,0,1 reshape → ["abcdef","",""]
↗️

If the second arg ends then the results end too:

"abcdef" 2,2 reshape → ["ab","cd"]
↗️

I was inspired by the mold sort challenge to make sure that trailing 0s work. This somewhat felt like cheating, but really this is the most intuitive behavior for the op. It takes hands on experience to truly understand how to make ops as general as possible, and I'm sure I haven't perfected every op. If you ever wish an op was a bit different and think it is an overall improvement you can cheat too! Let me know!

Transpose

You can transpose a list of different length lists, it will fill in missing values with the default value for that type:

"abcd","ef","1234" transpose → ["ae1","bf2","c 3","d 4"]
↗️

But it will truncate instead if there are no more values after a point:

"abc","1" transpose → ["a1","b","c"]
↗️

Get

get returns the ith element (0 indexed) or the default value for the type if out of bounds. Special behavior for negatives or out of bounds is likely less useful since it can be simulated using reverse or repeat concat.

SetDiff

setDiff removes the elements of the second arg from the first. If there are two occurrences of an element it will remove up to two of that element from the first, and so on for higher counts.

SortBy

It is stable and O(n log n).

Read / ReadAll

These ops ignore all non numbers and leading negative signs to return parsed numbers from a string. read is just the head of readAll.

read will return 0 if no numbers are present, if you wanted to detect that you could check the length of readAll.

Join

If you join a string (instead of a list of strings), it promotes each character to a string (rather an unvectorized promote which would be useless here).

"abc" " " * → "a b c"
↗️

ChunkWhen

This op is likely to be rarely used, but very useful when it is, and it is the only op that can allow you to split a list up by an arbitrary criteria. (Besides doing it manually which seems difficult). I have gone through several variations of this op over the years and settled on the current behavior as the most versatile and easy to use. You can accomplish things like grouping truthy elements together (removing falsey) and grouping adjacent equal elements very easily with this op.

It splits the first list after each falsey value from the second list.

"abcd" 1,0,1,1 chunkWhen → ["ab","cd"]
↗️

Because the value corresponding to 'b is 0, it split after that value.

Note that the last 1 is the op stops splitting when the second arg runs out and returns the remaining list of the first arg.

"abcd" 1,0,1 chunkWhen → ["ab","cd"]
↗️

But if the last value had instead been a 0, that would indicate the next list has started, so the behavior would be different:

"abcd" 1,0,1,0 chunkWhen → ["ab","cd",""]
↗️

Extra condition values are also ignored.

"ab" 1,1,1,1 chunkWhen → ["ab"]
"ab" 0,0,0,0 chunkWhen → ["a","b",""]

And as promised, here is how we could group truthy values:

"ab bc"; consDefault chunkWhen tail → ["ab","bc"]
↗️

The consDefault is necessary to align things correctly (but the value is ignored by the later tail). If you wanted to provide custom truhty behavior, just modify the duplicated value after the consDefault.

This will return empty lists if multiple falsey values are present in a row (just filter if you don't want those).

And here's how we could group adjacent equal elements (similarly you could also define equality differently by doing something after the dup :):

"aabbcdee" ::tail equal chunkWhen → ["aa","bb","c","d","ee"]
"abbcde" ::tail equal chunkWhen → ["a","bb","c","d","e"]

CharClass

charClass and isCharClass ops are not actually complicated, but they are a bit unusual so require some explanation.

charClass returns all the characters that are in the char class of each letter of the arg. So for example:

"an4" charClass → "abcdefghijklmnopqrstuvwxyz0123456789"
↗️

a and n are in the same char class (lower case alphabet letters), so only the first does anything, and 4 is present so all the digit characters are added.

The char classes are:

abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
space newline

The isCharClass op checks if any character is in any of these classes.

Slicing Ops

If these ops are called with the second arg being [char] (or char which will promote to [char]), then that substring is searched for and used in place of an index.

So d is dropUntilAfterSubstring instead of drop, etc.

"beatles" "at" d → "les"
↗️

g would get the substring if it matched (empty string otherwise).

# repeatedly matches substrings, effectively performing a splitKeepEmpties aka cut.

Uppercase versions of these take in a list of possible substrings to match the first of.

"beatles" "l","a" D → "tles"
↗️

(because the "a" was found first).

- (cutAny) is the uppercased version of #.

+ (scanAny) is all the matches found in a cutAny.

Iterate

iterate behaves like its Haskell counterpart. Note that it can accept multiple initial values. It is explained in the intro page's circular programming.

Expand / Iterate0

Expand is an iterate from the default value (whose rank depends on the capitalization of expand and the return type of the function)

Foldr

foldr is like iterate but starting from the right, and taking the first element as the final result. The Circular Programming doc explains how it works.

Meld / Foldr0

Meld is a foldr from the default value (whose rank depends on the capitalization of meld and the return type of the function)