protected static function coerceValue(TypeInterface $type, $value)
    {
        if ($type instanceof NonNullModifier) {
            // Note: we're not checking that the result of coerceValue is non-null.
            // We only call this function after calling isValidValue.
            return self::coerceValue($type->getWrappedType(), $value);
        }

        if (!isset($value)) {
            return NULL;
        }

        if ($type instanceof ListModifier) {
            $itemType = $type->getWrappedType();
            if (is_array($value)) {
                return array_map(function ($item) use ($itemType) {
                    return Values::coerceValue($itemType, $item);
                }, $value);
            } else {
                return [self::coerceValue($itemType, $value)];
            }
        }

        if ($type instanceof InputObjectType) {
            $fields = $type->getFields();
            $object = [];
            foreach ($fields as $fieldName => $field) {
                $fieldValue = self::coerceValue($field->getType(), $value[$fieldName]);
                $object[$fieldName] = $fieldValue === NULL ? $field->getDefaultValue() : $fieldValue;
            }

            return $object;
        }

        if ($type instanceof ScalarType || $type instanceof EnumType) {
            $coerced = $type->coerce($value);
            if (NULL !== $coerced) {
                return $coerced;
            }
        }

        return NULL;
    }