hacklang

How to define empty dict<string, int> in hacklang


I have following shape:

const type A = shape(
  'b' => dict<string, int>,
);

How do I create this shape with empty dict? For example from this function

function getA(): A {
  return shape(
    'b' => ???
  );
}

Solution

  • This is a part of the Hack array proposal

    A dict array is a container mapping a valid arraykey to a value. This mirrors the behavior of the Map class. This will be an array at runtime with extra restrictions to allow it to be used safely like a Map from the type checkers perspective. This focuses mainly on the desired runtime behavior, but if there are questions for how it will interact with the type checker I'll be happy to discuss. Literal Constructor

    A dict is constructed using the literal expression dict[].

    $dict = dict[]; // Empty dict-array
    $dict = dict['a' => 1, 'b' => 2, 'c' => 3]; // dict-array of 3 elements
    

    This is a static expression, so it can be used in a static initializer.

    class C {
      public static dict<string, int> $dict = dict[
        'a' => 1,
        'b' => 2,
        'c' => 3,
      ];
    }
    

    source : https://github.com/facebook/hhvm/issues/6452

    You don't need to define the type when creating dictionaries ( dict[] ) or vectors ( vec[] ) as generics are ignored in runtime

    Example :

    function spamMessages(dict<string, string> $messages): void
    {
        foreach($messages as $user => $message) {
            echo $user . ': ' . $message;
        }
    }
    
    function listUsers(vec<string> $users): void
    {
        foreach($users as $user) {
            echo '-' . $user;
        }
    }
    
    $messages = dict[
        'azjezz' => 'bring the cookies'
    ];
    
    $users = vec[
        'azjezz'
    ];
    
    spamMessages($messages);
    listUsers($users);
    

    In this example, the type checker is smart enough to see that $message have both keys and values of type string so it won't raise any errors.

    You can also create a dict array or a vector from a php-style array using the vec and dict helpers

    $array = [
        'a' => 1,
        'b' => 'c'
    ];
    
    $dict = dict($array);
    

    In the above example, the type checker knows about the type of the keys in $array, and since the dict helper is declare as dict<Tk, Tv>(KeyedTraversable<Tk, Tv> $array): dict<Tk, Tv> it knows that the returned dict array contains the same types for both keys and values.

    Sometimes you may not be able to tell the type checker about the types of array keys and values.

    Example:

    $array = json_decode('path/to/file.json');
    

    You know that the json files contains an array of string keys and values for example, but here type checker thinks that $array is of type mixed since there's no way to actually tell what's inside the file.

    In order to get over this, you can use the as operator to tell the type checker about $array type

    $array = json_decode('path/to/file.json') as array<_, _>;
    

    Notice that here we used _ instead of string and they type checker is going to assume that $array now is of type array<arraykey, mixed>, the reason is generics are not supported with the as operator as of now, but this might changed in future so we can do as array<string, string>

    Later you can create a dict out of that array :

    $dict = dict($array);
    

    If you for example know that there's no way for that file to contain anything beside string keys and values and want to force that type you can use the following helper to cast it to a dict of type dict<string, string>, but I won't recommend it as it may cause an error while casting non-stringable objects or resources to string.

    // KeyedContainer = array, dict, maps and other keyed containers 
    function forceStringDict(KeyedContainer<arraykey, mixed> $container): dict<string, string>
    {
        $dict = dict[];
        foreach($container as $k => $v) {
            $dict[(string) $k] = (string) $v;
        }
        return $dict;
    }
    
    $dict = forceStringDict($dict);