# Types

## Truthiness

-   Lists are truthy if non empty.
-   Chars are truthy if non whitespace.
-   Ints are truthy if non zero.

This is used by ops like `not`, `takeWhile`, etc.

## Default Value

Ops like `consDefault` use the concept of a default value, but so do things like `head` (for empty list).

-   Int is 0
-   Char is space
-   List is empty list

If you force a scalar out of the empty type it will default to being an int (0).

## Coercion

Any time you use an op that expects a `char` and you give it an `int` it simply first converts the `int` to a string.

For example:

    1,2,3 " "* -> "1 2 3"
    "abc" 3 append -> "abc3"

## Promotion

You can give ops that require lists a value whose rank is too low and it will automatically enclose that value in a list of one element, equivalent to using `just`.

For example:

    "asdf" 'z append -> "asdfz"

The char is first turned into a string of one character.

This would never be useful for some ops, like `head` since it would just return the original value, so it will error in cases like these.

## Special Repetitions

There are a a few ops (`% cut`, `& reshape`, and `* join`) whose type signature might not have been what you expected. For example join, the arg types are: `[[char]] {[char]}`, shouldn't they be `[[char]] [char]` ?

The `{}` is just like `[]` except if the rank is too low, then repeat the arg instead of promote it. This will act like how you expect, but provide additional ability to use a different value at each step of the op.

So for string, something like:

    "i","am","a","sentence" " " * -> "i am a sentence"

Works as expected. But we could have changed the second thing inserted (and even terminated earlier too).

    "i","am","a","sentence" " ","-" * -> "i am-a"

Here's just another example for inspiration on how to use them: we could get all of the non numbers of a string by cutting out all of the numbers:

    "a1,2--33b" dup readAll str cut -> ["a",",","--","b"]

Or a reshape example:

    10 countTo 1,2,3,4 reshape -> [[1],[2,3],[4,5,6],[7,8,9,10]]

## Ops With Two Generic Types

For ops that take two different generic types, such as `sortBy`, `ifElse`, etc. Capitalization / commas still control the vectorization level. But if one arg's excess rank is smaller than the other, then instead of broadcasting it will just reduce that small args required rank.

For example if you wanted to sort some strings by length:

    "abc","xy" : len SortBy -> ["xy","abc"]

The effective type of `SortBy` becomes `[[a]] [b] to [[a]]`

You can still manually broadcast that second arg using `repeat` if that is actually what you wanted.

Here `len` reduced the rank we wish to compare by, but this also works if we are increasing the rank we wish to compare by. For example suppose we wanted to filter for composite numbers.

We can calculate all of a number's divisors with:

    6 ; countTo : countTo mod not filter -> [1,2,3,6]

So we can get composite numbers by filtering when the list of composites is not empty after dropping 2:

    10 countTo : ; countTo : countTo mod not filter 2 drop Filter -> [4,6,8,9,10]

Note that `Filter` was used instead of `filter` since the second arg's rank was a 2d list and we didn't want to vectorize into it.

If both args have a type too low this it would only raise the type of the `b` type, for example:

    1,2,3 0,0,0 Filter -> [1]

I would find it more intuitive if capitalization only controlled the type of the `a` type since that is what is returned. But this wouldn't be optimal since cases like the following would require explicit repetition of the 2nd or 3rd arg to do what you want:

    "" 1 2 IfElse -> 2

Aside from this, it should do what you want so you shouldn't have to think about it (just make sure to capitalize based on the higher rank of the two args). If it gets confusing you should look at the implicit ops and types of everything using the graphical representation of your program generated by the [Online Interpreter](onlineinterpreter.html).

## Low Rank Overrides

There are some ops that would be useless when called with an argument whose rank is too low, such as `T` for unvectorized `tail`. It would be useless since promoting a single value to a list and then taking the tail will always be the empty list (which isn't useless per se, but there are other ways to get an empty list so all but one of these ways is useless). So we overload this behavior to do something more useful instead, in this case it is `rangeTo`:

    4T -> [0,1,2,3]
    "hi","there" T -> ["there"]

Most of the low rank overrides can also be used vectorized once, but not twice since then ranks would be high enough to mean the non-overriden version of the character. The number of times they can vectorize is shown in the quickref by the subscripted number in the op arg's types. For op overloads with 2 args, only at least one arg needs to satisfy its subscript arg requirements.

If you do want to use `rangeTo` on a rank 2 list (twice vectorized), you still can using `T,` (or equal number of `,`s as you exceed the subscripted number by).

    1,2,3T -> [[0],[0,1],[0,1,2]]
    1,2,3,,4T, -> [[[0],[0,1],[0,1,2]],[[0,1,2,3]]]

This also works with lower case overrides like `sqrt` (`u`)

    4,9,,100,1000 u, -> [[2,3],[10,31]]

A few overloads would not work well with comma adding, so they provide an alternate 2 char way of specifying them. For example `V (, min`, you could use just `V` in most cases, but if you wanted to take the min of two lists (unvectorized), you would need to use `(,` and then unvectorize it once, so it would be `(,,` although that does end up being the same length as just saying `Min`. The two char ways (or full name) are just regular ops not overloads.

It is theoretically possible to create even more low rank overrides than there currently are, but the goal of iogii is to be simple, not fully optimized for code golf. It also gets very complicated to do this since they need to obey certain invariants for static typing of circular programs to work correctly (`S` would otherwise be a useful overload).

## Static Typing

Believe it or not iogii is statically typed. Static typing is very useful for lazy programs and I would argue that homogeneous lists are more useful than heterogenous ones for code golf, especially since we can overload and do promotion/coercion this way. It also eliminates the problem of the type of an empty list which sometimes should be known!

There's not much downside since iogii is built so that you never need to specify a type and it can always deduce the correct types. In order to get inference to work with circular programs, all ops where carefully selected so that they obey certain invariants (including overloads since you don't know which op will be selected until types are inferred).

That invariant is:

-   If increasing any ops args' type, then the resulting type must also increase or stay the same.

Types are defined in this order `empty`, `int`, `char`. So by increasing I mean walking up in that list.

-   If increasing any op args' rank, then the resulting rank must also increase or stay the same (and the base type must remain the same).

Most of the time this is how I would want ops to be defined anyways. However, this is why the `read` op symbol isn't the same as the `str` op symbol despite them being inverses. Because doing so would violate the first invariant.

## Nil Type

The type of `nil` or the first value in an `Expand` / `Meld` is a list of the empty type. It has its own type so that it can become any time without coercion. Its rank will automatically become high enough for any operation.

## Errors

Iogii typically tries to avoid giving you an error and will instead return the default falsey value for that type.

For example:

    0 0/ -> 0
    1,2,3 5 get -> 0

Negative values don't make sense for many ops like take, drop, etc. In this case they will just behave like 0.
