Skip to content

Jesse's Software Engineering Blog

Nov 26

Jesse

Hacklang Generics

Facebook released Hacklang in early 2014 for use with HHVM, and has since announced that almost their entire code base has been ported from PHP to Hack. Hack interoperates seamlessly with PHP code and offers various enhancements, such as static typing, collections, asynchronous processing, etc. As a PHP developer migrating to Hack, the language was easy to pick up, however some considerations needed to be made for some of the new language features.

Statically Typed Languages

Generics are essential to statically typed languages, and have been available in the Java JDK since 2004. Since PHP is a dynamically typed language, there has never been a need for generics, and is thus a new principle for many PHP developers. The focus of this article is not to describe how to use generics, rather to demonstrate to PHP developers why they are needed and where to find more information on how to correctly use them.

The main difference between statically and dynamically typed languages:

In a statically typed language, the types of variables are known at compile time. Once a variable name has been bound to a type, it can only be bound to objects of that type. In dynamically typed languages, the variable types are determined at runtime and can thus be assigned different types.

# statically typed
int var = 1
var = "str" // illegal

# dynamically
$var = 1;
$var = "str"; // ok

Generics provide a bit of flexibility with statically typed languages and eliminates the need for a lot of instanceof() calls and object type casting.

Purpose of Generics

Another feature of Hacklang, which is outside the scope of this article, are collections. In an attempt to eliminate the over use of arrays Hack introduced various collection objects including Vectors. Vectors are only associated with a certain type of items, for example a Vector of integers would be specified with the <int> tag. In some scopes/modes Hack infers types, so the type declarative is not always needed.

$x = Vector<int>{5,10};
$y = Vector<string>{'a', 'b', 'c', 'd'};

So what would happen if we needed an object for storing various different types

<?hh //strict

class Drive
{
	protected Vector<int> $v_int = Vector{};
	
	protected Vector<string> $v_str = Vector{};

	public function store(mixed $key, mixed $val): void
	{
		if (is_string($val)) {
			$this->v_str[$key] = $val;
		} else if(is_int($val)) {
			$this->v_int[$key] = $val;
		}
	}
}

Notice that the Vectors need to be type casted and a separate Vector is needed for each data type. Not an ideal solution. What is needed is the ability for objects to be able to store different types, determinable at run time. NOTE: This can also be achieved using mixed, but generics is the recommended approach.

Using Generics

The code for this example can be found on Github.

As outlined in the previous section, developers need a flexible, clean way to bypass the strictness of statically typed variables, and associate objects with types dynamically at run time. To achieve this with generics we would update the Drive class:

<?hh //strict

class Drive <Tk, Tv>
{	
	protected Map<Tk, Tv> $storage = Map{};

	/**
	 * Place item into storage
	 */
	public function store(Tk $key, Tv $val): void
	{
		$this->storage[$key] = $val;
	}
}

Notice instead of using mixed, there is type T, which allow for dynamic types. Instead of hard coding the collection to a specific key/value type, we are able to use whatever type as allowed by the collection. This example also used the Map collection which allows for both string and integer key types. One thing to consider is that generics can be applied on the function level as well as the class/interface level, but rarely will they be used on both a class and functions of that class. It’s generally one or the other.

Further Reading

This article and code sample was created simply for the purpose of demonstrating the need for generics in statically typed languages. The Hacklang documentation goes into much further detail of how to use generics, and provides various use cases. Some important generics features that should be researched further:

Generics with interfaces and traits
http://docs.hhvm.com/manual/en/hack.generics.traitsandinterfaces.php

Examples of why to use generics over the mixed type
http://docs.hhvm.com/manual/en/hack.generics.mixedcompat.php

Constraints, allow specification of which object types can be used with generics. i.e. to treat a generic as a certain object type. Goes with generics type inference.
http://docs.hhvm.com/manual/en/hack.generics.typeinference.php
http://docs.hhvm.com/manual/en/hack.generics.constraints.php

Variance refers to how the subtyping between objects works i.e. the ability to use more or less derived objects. Covariance preserves assignment compatibility while contravariance reverses assignment compatibility.
http://docs.hhvm.com/manual/en/hack.generics.covariance.php

Considerations for different objects being passed as generics that are not all compatiable, leads to casting and/or code rewriting.
http://docs.hhvm.com/manual/en/hack.generics.openandclosedtypes.php

Blog Powered By Wordpress