Emojicode Documentation 1.0 beta 2

Classes & Value Types

Emojicode features three kind of types that feature characteristics of object-orientation: Classes, Value Types and Enumerations. This chapter is soley dedicated to classes and value types. A separate chapter is devoted to enumerations, which have a lot in common with value types.

Classes versus Value Types

There are two significant differences between classes and value types:

This makes value types suitable when only the actual data represented matters and not the identity of the object. In other words, you should use value types when you only care about the values they carry and not about whether you have a particular instance of the value type.

Dates or mathematical vectors are good examples of types that should be value types, whereas a type representing a customer should be a class, as it does matter with which customer you’re dealing and not just the data it holds. Think of it like this: There may be many customers named β€œJohn Smith” but the customers are still different people and therefore are represented by different objects.

Defining a Class

Let us define a class representing a customer:

πŸ‡ πŸ‘©β€πŸ’Ό πŸ‡

πŸ‰

As mentioned previously, classes feature inheritance. We can therefore also declare a subclass of our πŸ‘©β€πŸ’Ό class:

πŸ‡ πŸ‘©β€πŸš€ πŸ‘©β€πŸ’Ό πŸ‡

πŸ‰

Some of our customers are astronauts, so we created the subclass πŸ‘©β€πŸš€. To make a class a subclass, denote its superclass behind the new classes name. If you don’t provide a superclass, the class doesn’t have one.

Defining a Value Type

Naturally, we also need to maintain our customers credit card information to be able to bill them. Credit card information is a great example of a value type so let’s define one:

πŸ•Š πŸ’³ πŸ‡

πŸ‰

The definition of a value type is quite similar to the definition of a class. We just used πŸ•Š instead of πŸ‡. Furthermore, value types cannot have a supertype.

Instance Variables

We have declared various types now, but so far these are pretty useless as they do not store any information at all.

Let us change this by adding instance variables. The normal syntax for declaring variables is used in value types and classes too:

πŸ•Š πŸ’³ πŸ‡
  πŸ–πŸ†• number πŸ”‘
  πŸ–πŸ†• expiration_date πŸ”‘
  πŸ–πŸ†• security_code πŸ”‘
πŸ‰

We have added some variables to store the credit card information. (We do not maintain that this is a particularly good way of structuring credit card information but this is just an example. πŸ™ƒ)

πŸ‡ πŸ‘©β€πŸ’Ό πŸ‡
  πŸ–πŸ†• firstname πŸ”‘
  πŸ–πŸ†• lastname πŸ”‘
  πŸ–πŸ†• creditcard πŸ’³
πŸ‰

πŸ‡ πŸ‘©β€πŸš€ πŸ‘©β€πŸ’Ό πŸ‡
  πŸ–πŸ†• days_in_space πŸ”’
πŸ‰

We have also added some information to the normal customer πŸ‘©β€πŸ’Ό and the astronaut customer πŸ‘©β€πŸš€.

Instance variables are private to the instance and cannot be accessed from outside but only in initializers and methods. If you want to access instance variables from outside you have to write getters and setters. Instance variables are also kept private from subclasses.

Default Initialization Value

You can also specify a value to which an instance variable will be initialized:

πŸ‡ πŸ‘©β€πŸ’Ό πŸ‡
  πŸ–πŸ†• firstname πŸ”‘ ⬅️ πŸ”€SusanπŸ”€
  πŸ–πŸ†• lastname πŸ”‘ ⬅️ πŸ”€RodgersπŸ”€
  πŸ–πŸ†• creditcard πŸ’³ ⬅️ πŸ†•πŸ’³ πŸ”€48829284848291πŸ”€ πŸ”€12/22πŸ”€ πŸ”€513πŸ”€β—οΈ
πŸ‰

Note that the expressions are not evaluated in the context of the initializer. Furthermore, these expressions are always evaluated when an initializer is called.

Syntax

We have summarized the syntax here as it is a great deal of definitions and we didn’t want to clutter the previous sections.

type-definition ⟢ [documentation-comment] [🌍] [πŸŽπŸ›’] [πŸ”] [πŸ“»] type-definition-main
type-definition-main ⟢ class | value-type | protocol | enum
class ⟢ πŸ‡ type-identifier [generic-parameters] [superclass] type-body
type-body ⟢ πŸ‡ type-body-declarations πŸ‰
type-body-declarations ⟢ type-body-declaration | type-body-declaration type-body-declarations
type-body-declaration ⟢ type-body-attributes type-body-declaration-main
type-body-attributes ⟢ [documentation-comment] [πŸ₯―] [⚠️] [πŸ”] [βœ’οΈ] [πŸ‡] [☣️] [πŸ–] [πŸ”‘] [🎍πŸ₯‘] [access-level]
type-body-declaration-main ⟢ instance-variable-declaration | method | initializer
type-body-declaration-main ⟢ protocol-conformance | enum-value
type-body-declaration-main ⟢ deinitializer
instance-variable-declaration ⟢ declaration [⬅️ expression]
superclass ⟢ type
value-type ⟢ πŸ•Š type-identifier [generic-parameters] type-body
initializer ⟢ πŸ†• [initializer-name] [init-parameters] [error-type] body
initializer-name ⟢ ▢️ emoji-id
init-parameters ⟢ init-parameter | init-parameter init-parameters
init-parameter ⟢ [🎍πŸ₯‘] [🍼] variable type
body ⟢ block | external-link-name
access-level ⟢ πŸ”“ | πŸ”’ | πŸ”

Initializers

Initializers are responsible to prepare an instance for use and when you create a new instance of the type.

In an initializer all instance variables must be initialized. Remember that variables of an optional type are automatically initialized to no value, which is also true for instance variables.

Let us define an initializer:

πŸ•Š πŸ’³ πŸ‡
  πŸ–πŸ†• number πŸ”‘
  πŸ–πŸ†• expiration_date πŸ”‘
  πŸ–πŸ†• security_code πŸ”‘

  πŸ†• a_number πŸ”‘ an_expiration_date πŸ”‘ a_security_code πŸ”‘ πŸ‡
    a_number βž‘οΈπŸ–number
    an_expiration_date βž‘οΈπŸ–expiration_date
    a_security_code βž‘οΈπŸ–security_code
  πŸ‰
πŸ‰

Now that was some tedious work, assigning all that instance variables. Because it is common to initialize instance variables from parameters, Emojicode provides a shortcut: 🍼.

🍼 is placed in front of the variable name of an parameters. Its value is then copied into the instance variable with the same name:

πŸ†• 🍼 number πŸ”‘ 🍼 expiration_date πŸ”‘ 🍼 security_code πŸ”‘ πŸ‡πŸ‰

This is much better. Let us add an initializers to πŸ‘©β€πŸ’Ό as well.

πŸ‡ πŸ‘©β€πŸ’Ό πŸ‡
  πŸ–πŸ†• firstname πŸ”‘
  πŸ–πŸ†• lastname πŸ”‘
  πŸ–πŸ†• creditcard πŸ’³

  πŸ†• 🍼 firstname πŸ”‘ 🍼 lastname πŸ”‘ 🍼 creditcard πŸ’³ πŸ‡πŸ‰
πŸ‰

Before implementing an initializer for πŸ‘©β€πŸš€ we must review one additional rule: If you’re writing an initializer for class that has a superclass you must call an initializer of the superclass. ‴️ is used for that:

πŸ‡ πŸ‘©β€πŸš€ πŸ‘©β€πŸ’Ό πŸ‡
  πŸ–πŸ†• days_in_space πŸ”’

  πŸ†• 🍼 days_in_space πŸ”’ firstname πŸ”‘ lastname πŸ”‘ creditcard πŸ’³ πŸ‡
    β€΄οΈπŸ†• firstname lastname creditcard❗️
  πŸ‰
πŸ‰

Let us take a closer look at ‴️ : The first thing it expects is the name of the initializer of the superclass you wish to call.

Instantiation

We have defined a value type and two classes, defined how to inititalize them, but we have yet to actually instantiate (get an instance) of them. Instatiation is performed with πŸ†•.

Its syntax is:

instantiation ⟢ πŸ†• type-expr [initializer-name] [arguments] mood

Let us instantiate a credit card information πŸ’³:

πŸ†•πŸ’³ πŸ”€48829284848291πŸ”€ πŸ”€12/22πŸ”€ πŸ”€513πŸ”€β—οΈ ➑️ credit_card

Directly after πŸ†• comes πŸ’³, the name of the type we want to instantiate.

The following expressions are arguments to the initializer. ❗️ denotes the end of the arguments.

Having instantiated a credit card, we can also instantiate a customer:

πŸ†•πŸ‘©β€πŸ’Ό πŸ”€MickeyπŸ”€ πŸ”€MouseπŸ”€ credit_card❗️ ➑️ customer_mouse
πŸ†•πŸ‘©β€πŸš€ 3216 πŸ”€Jean-LucπŸ”€ πŸ”€PicardπŸ”€ credit_card❗️ ➑️ astronaut_picard

Named Initializer

Caution

Note that initializers support overloading, which makes the use of named initializers unnecessary in most cases, and will be discussed in the next chapter.

For completeness, let’s add an initializer with a name:

πŸ‡ πŸ‘©β€πŸ’Ό πŸ‡
  πŸ–πŸ†• firstname πŸ”‘
  πŸ–πŸ†• lastname πŸ”‘
  πŸ–πŸ†• creditcard πŸ’³

  πŸ†• 🍼 firstname πŸ”‘ 🍼 lastname πŸ”‘ 🍼 creditcard πŸ’³ πŸ‡πŸ‰

  πŸ†• β–ΆοΈπŸ§œβ€β™€οΈ 🍼 firstname πŸ”‘ 🍼 creditcard πŸ’³ πŸ‡
    πŸ”€MermaidπŸ”€ ➑️ πŸ–lastname
  πŸ‰
πŸ‰

In the above example, you can see an initializer named πŸ§œβ€β™€οΈ. In contrast to the other initializer, it does not take the lastname. Instead it initializes lastname to the string Mermaid.

We can use the πŸ§œβ€β™€οΈ initializer like this:

πŸ†•πŸ‘©β€πŸ’Όβ–ΆοΈπŸ§œβ€β™€οΈ πŸ”€ArielπŸ”€ credit_card❗️ ➑️ ariel

Methods

Methods are functionality bound to a specific type: a class or value type.

The syntax to define a method is:

method ⟢ identification [generic-parameters] [parameters] [return-type] [error-type] body
identification ⟢ mood emoji-id | binary-operator
mood ⟢ ❗️ | ❓ | ➑️
parameters ⟢ parameter | parameter parameters
parameter ⟢ [🎍πŸ₯‘] variable type
return-type ⟢ ➑️ type

Let us define a method for πŸ‘©β€πŸ’Ό to print an invoice:

❗️ πŸ’Έ total πŸ’― πŸ‡
  πŸ˜€ πŸ”€InvoiceπŸ”€β—οΈ
  πŸ˜€ πŸ”€To 🧲firstname🧲 🧲lastnameπŸ§²πŸ”€ ❗️
  πŸ˜€ πŸ”€Total: πŸ§²πŸ”‘total 2β—οΈπŸ§²πŸ”€β—οΈ
  πŸ˜€ πŸ”€Your credit card will be charged. πŸ”€β—οΈ
πŸ‰

Returning Values

Methods can, of course, also return a value. Unless you declare a return type, the method is assumed to not return a value.

Let us add a method to πŸ’³ that returns a value:

❗️ πŸ”– ➑️ πŸ”‘ πŸ‡
  ↩️ number
πŸ‰

This method simply returns the credit card number. It uses the return statement ↩️ to return the value from the method.

return ⟢ ↩️ expression | ↩️↩️

Returning from Methods without Return Value

You can also return from a method that does not have a return type at any point using ↩️↩️.

For example, this method will never print Cheap prices! because it immediately returns:

❗️ πŸ›Ž πŸ‡
  ↩️↩️
  πŸ˜€ πŸ”€Cheap prices!πŸ”€β—οΈ
πŸ‰

Method Moods

In Emojicode every method has a mood. The methods we have previously defined, are of imperative mood as we used ❗️. The other mood is interrogative. Interrogative methods are defined with ❓ instead.

The mood is like part of the name of the method. You can have an interrogative and imperative method with the same basic name.

Let us define an interrogative method for the πŸ‘©β€πŸš€ class:

❓ πŸš€ ➑️ πŸ‘Œ πŸ‡
  ↩️ days_in_space ▢️ 0
πŸ‰

This method returns true if the astronaut ever boarded a rocket. We can define an imperative method with the same name that allows us to change the number of days the astronaut spent in space:

❗️ πŸš€ days πŸ”’ πŸ‡
  days ➑️ πŸ–days_in_space
πŸ‰

Calling Methods

We have defined two methods, but we have yet to fully understand how to call a method.

We’ll have a look at some examples first:

πŸ’Έ astronaut_picard 109.12❗️
πŸ’Έ customer_mouse 59.00❗️
πŸš€ astronaut_picard❓ πŸ’­ Was he ever in space?
πŸš€ astronaut_picard 6390❗️ πŸ’­ Change the number of days to 6390

As you can see above, the syntax to call a method is special:

method-call ⟢ emoji-id callee [generic-arguments] [arguments] mood
method-call ⟢ emoji-id mood
callee ⟢ expression
arguments ⟢ expression [arguments]

If an emoji occurs that is not reserved for a built-in statement or expression (e.g. ↩️ or 🚨), it is considered a method call. The compiler then expects an expression, evaluating to a value that has method with the provided name. Then arguments are expected until either ❗️ or ❓ occurs.

In a method or initializer call all arguments are evaluated from left to right, but after the callee in the case of a method call.

A method call expression evaluates to the value the method returned. If the method does not declare a return type, the call expression returns a value of type no return, which is neither compatible to any type nor does it offer any functionality.

Hint

Note that methods like initializers support overloading as we will see in the next chapter.

This Context

You will naturally need to access the instance on which the method was called. This is what πŸ‘‡ is for.πŸ‘‡ returns the value on which the method or initializer was called.

For example, we could add a method to the πŸ‘©β€πŸš€ class that bills an astronaut if has traveled to space:

❗️ πŸ›Έ πŸ‡
  β†ͺ️ πŸš€ πŸ‘‡β“ πŸ‡
    πŸ’ΈπŸ‘‡ 100❗️
  πŸ‰
πŸ‰

Note that in an initializer, you can’t use πŸ‘‡ before the object is fully initialized, that is before all instance variables were set and the superinitializer was called. If this was allowed, you could call methods on the instance which might access instance variable that had not been initialized yet.

this ⟢ πŸ‘‡

Note that when you want to call a method on πŸ‘‡ that does not take any arguments you can omit πŸ‘‡:

πŸ™‹β—

Assignable Methods

You can also define instance methods to which values can be assigned.

Consider that to get a value from a list the 🐽 method is used as in this example:

🐽 a_list 1❗️

It would be very intuitive, if we could β€” to assign a value β€” write this:

πŸ”€CocoπŸ”€ ➑️ 🐽 a_list 1❗️

And, indeed, we can. This works, because Emojicode allows us to define methods that can be assigned to. Although this might sound complicated to you, in fact, it isn’t. Take a look at this example, in which an assignee method is defined.

❗️ 🐽 index πŸ”’ ➑️ 🍬Element πŸ‡
  πŸ’­ ...
πŸ‰

➑️ 🐽 assigned_value Element index πŸ”’ πŸ‡
  πŸ’­ ...
πŸ‰

In this example two methods are defined. The first one is rather obviously just the getter we used before. The second method, on the other hand, uses the assignment operator (➑️) instead of ❗️. This indicates to the compiler that this method should be called, when an assignment to a method of the name 🐽 occurs.

The first argument of the assignee method is always the value that is being assigned. It must not be provided on the right hand side of the assignment, as we have seen in the assignment example before. The method itself than is free to do whatever it needs to do with one limitation: It may not return a value.

method-assignment ⟢ expression ➑️ method-call

Mutability of Value Types

We have seen examples of methods that modify class objects, but we have not seen any examples of changing the instance variables of a value type. There’s a good reason why: Value types instance cannot be arbitrarily modified.

Methods of value types that wish to mutate instance variables must be attributed with πŸ–. Let us define a method for the πŸ’³ type that allows us to update the card’s security code:

πŸ–β—οΈπŸ— code πŸ”‘ πŸ‡
  code βž‘οΈπŸ–security_code
πŸ‰

If we hadn’t used the πŸ– attribute, the compiler would emit an error when compiling this. Of course, we can only call a method marked with πŸ– on πŸ‘‡ if the method we are in is attributed with πŸ– too. Thus the following would not work:

πŸ•Š πŸ’³ πŸ‡
  πŸ–πŸ†• number πŸ”‘
  πŸ–πŸ†• expiration_date πŸ”‘
  πŸ–πŸ†• security_code πŸ”‘

  πŸ–β—οΈπŸ— code πŸ”‘ πŸ‡
    code βž‘οΈπŸ–security_code
  πŸ‰

  β—οΈπŸ¦  code πŸ”‘ πŸ‡
    πŸ—πŸ‘‡ πŸ”€000πŸ”€β—οΈ πŸ’­ We cannot call a mutating method from a non-mutating one.
  πŸ‰
πŸ‰

We have defined a mutating method. We should call it as well, which brings us to another important aspect of value type mutability:

Only value types in mutable variables are mutable.

Let’s see an example:

πŸ†•πŸ’³ πŸ”€48829284848291πŸ”€ πŸ”€12/22πŸ”€ πŸ”€513πŸ”€β—οΈ ➑️ πŸ–πŸ†•credit_card
πŸ—credit_card πŸ”€126πŸ”€β—οΈ

This is perfectly fine, while the below example will not compile as credit_card is not mutable:

πŸ†•πŸ’³ πŸ”€48829284848291πŸ”€ πŸ”€12/22πŸ”€ πŸ”€513πŸ”€β—οΈ ➑️ credit_card
πŸ—credit_card πŸ”€789πŸ”€β—οΈ

Since instance variables are always mutable, you can always call mutating methods on the values of instance variables.

The return of a method is always immutable. The following code does not work:

πŸ•Š 🌼 πŸ‡
  πŸ‡β—οΈπŸ’³ ➑️ πŸ’³ πŸ‡
    ↩️ πŸ†•πŸ’³ πŸ”€48829284848291πŸ”€ πŸ”€12/22πŸ”€ πŸ”€513πŸ”€β—οΈ
  πŸ‰
πŸ‰

🏁 πŸ‡
  πŸ—πŸ’³πŸ•ŠπŸŒΌβ—οΈβ—οΈ
πŸ‰

Type Methods

It’s possible to define type methods which are called on the type rather than on an instance of the type. Still, type methods are also inherited by subclasses.

Type methods are defined like normal methods but with the πŸ‡ attribute. As for example:

πŸ‡ πŸ• πŸ‡
  πŸ“— Return available pizza dishes. πŸ“—
  πŸ‡β—οΈ πŸ“œ ➑️ πŸ¨πŸšπŸ”‘πŸ† πŸ‡
    ↩️ 🍿 πŸ”€MargheritaπŸ”€ πŸ”€TonnoπŸ”€ πŸ”€Quattro FormaggiπŸ”€ πŸ†
  πŸ‰
πŸ‰

πŸ•Š πŸ’³ πŸ‡
  πŸ“— Returns some credit card providers. πŸ“—
  πŸ‡β—οΈ 🏒 ➑️ πŸ¨πŸšπŸ”‘πŸ† πŸ‡
    ↩️ 🍿 πŸ”€VisaπŸ”€ πŸ”€MasterCardπŸ”€ πŸ”€DiscoverπŸ”€ πŸ†
  πŸ‰
πŸ‰

We can call our type methods like this:

πŸ“œπŸ‡πŸ•β—οΈ
πŸ’πŸ•ŠπŸ’³β—οΈ

This calls the type method πŸ“œ on the class πŸ•, which we just defined above. In class type methods, πŸ‘‡ represents the type value on which the method was called. To learn more about what this means please see Types As Values.

Access Levels

Access Levels describe from which context a method or initializer can be called. There are three access levels:

The following example cannot be compiled, as πŸ™‹ is a private method and can therefore not be called from 🏁.

πŸ‡ 🐟 πŸ‡
  πŸ†• πŸ‡πŸ‰

  πŸ”’ ❗️ πŸ™‹ πŸ‡
    πŸ˜€ πŸ”€I’m a fish.πŸ”€β—οΈ
  πŸ‰
πŸ‰

🏁 πŸ‡
  πŸ†•πŸŸβ—οΈ ➑️ fish
  πŸ™‹ fish❗
πŸ‰

If you do not specify an access level the method will default to πŸ”“, unless it overrides another method.

Deprecation

From time to time methods or initializers need to be deprecated. Emojicode allows you to mark a method or initializer as deprecated with the ⚠️ attribute.

The compiler will emit a warning wherever a deprecated method or initializer is used.

Inline

You can attribute a method or an initializer with πŸ₯―, which indicates to the compiler that it could be advantegous to inline the method. This attribute furthermore causes the compiler to include the function body in the interface file if one is generated.

← Previous Next Up: β€œOverloading” β†’
Something not quite right? Improve this page