Quais são alguns limites estilísticos razoáveis ​​na inferência de tipo?

8

O C ++ 0x adiciona um suporte bastante abrangente a inferência de tipos. Estou extremamente tentado a usá-lo em todos os lugares possíveis para evitar repetições indevidas, mas estou me perguntando se a remoção de informações explícitas de tipo em todo o lugar é uma boa idéia. Considere este exemplo bastante artificial:

Foo.h:

#include <set>

class Foo {
private:

    static std::set<Foo*> instances;

public:

    Foo();
    ~Foo();

    // What does it return? Who cares! Just forward it!
    static decltype(instances.begin()) begin() {
        return instances.begin();
    }

    static decltype(instances.end()) end() {
        return instances.end();
    }

};

Foo.cpp:

#include <Foo.h>
#include <Bar.h>

// The type need only be specified in one location!
// But I do have to open the header to find out what it actually is.
decltype(Foo::instances) Foo::instances;

Foo() {

    // What is the type of x?
    auto x = Bar::get_something();

    // What does do_something() return?
    auto y = x.do_something(*this);

    // Well, it's convertible to bool somehow...
    if (!y) throw "a constant, old school";

    instances.insert(this);

}

~Foo() {
    instances.erase(this);
}

Você diria que isso é razoável ou é completamente ridículo? Afinal, especialmente se você está acostumado a desenvolver em uma linguagem dinâmica, não precisa se preocupar muito com os tipos de coisas e pode confiar que o compilador detectará qualquer abuso flagrante do sistema de tipos. Mas para aqueles que dependem do suporte do editor para assinaturas de métodos, você está sem sorte; portanto, usar esse estilo em uma interface de biblioteca é provavelmente uma prática muito ruim.

Acho que escrever coisas com todos os tipos possíveis implícitos, na verdade, facilita muito o meu código, porque remove quase toda a desordem usual do C ++. Sua milhagem pode, é claro, variar, e é sobre isso que estou interessado em ouvir. Quais são as vantagens e desvantagens específicas do uso radical da inferência de tipo?

Jon Purdy
fonte
Na verdade, você fazer necessidade de cuidados sobre os tipos se você está em desenvolvimento em uma linguagem dinâmica; se você tiver o tipo de algo errado, não descobrirá até atingir essa seção do código em tempo de execução.
Larry Coleman
@ Larry: Claro. Mas não no sentido em que você precisa prever o tipo exato e a cadeia de herança de cada objeto em uso.

Respostas:

5

Sendo principalmente um programador Python, compartilho a mentalidade de que o programador não precisa saber o tipo exato. No caso de C ++, especialmente quando se lida com modelos de modelos com modelos ... É claro que isso não acontece porque detesto a digitação estática (não considero - considero Haskell uma das melhores coisas desde o pão fatiado, em parte devido à sua digitação estática), mas porque não me importo com o tipo exato. Por que, não é bem demonstrado por exemplos que usam nomes como fooou get_stuff(). Então, vamos escolher algo mais próximo da realidade:

auto users = get_users();
vector<decltype(users[0])> blocked_users;
/* I'm not a C++ guru, much less C++0x so forgive me if type
   inference and foreach must be combined differently */
for (auto user : users) {
    if (check_name(user.name)) blocked_users.push_back(user)
}

Não é uma anotação de tipo único, mas é perfeitamente claro o que faz, certo? Esse código não se importa com o tipo users, apenas precisa de algum intervalo para iterar sobre o que contém itens com os namequais pode ser alimentado check_name. Nada mais. Ninguém se importa com os 100-ish caracteres dos nomes de tipo que você teria que digitar de outra forma.

O mesmo se aplica à maioria dos códigos com nomes significativos. O exemplo da pergunta não é claro, mas também não seria claro com nomes de tipo explícitos, porque nem o contexto nem os identificadores fornecem qualquer indicação do que está acontecendo. Use identificadores significativos e o código pode ser entendido independentemente das anotações de tipo explícitas.


fonte
3

A razão pela qual eles trouxeram inferência foi impedir que as pessoas escrevessem códigos como:

foo().bar().baz().etc();

Quando você poderia escrever:

Foo f = foo();
Bar b = f.bar();
...

Exceto que Foo seria algum tipo de modelo longo, então torna-se melhor escrever:

auto f = foo();
auto b = f.bar();
...

Portanto, como regra geral, se o uso de auto faz com que você escreva o código como acima, em vez de um código semelhante ao primeiro exemplo, use-o. Caso contrário, continue adicionando definições explícitas.

dan_waterworth
fonte
Faz sentido. Eu me vejo usando autoem muitos casos em que tenho certeza de que outros seriam explícitos, mas não vejo o que há realmente de errado, por exemplo auto f = new SomethingLong(), porque é óbvio o que a expressão retorna. Eu só estou me perguntando onde traçar a linha.
Jon Purdy
Tudo bem se SomethingLongnão fizer parte de nenhuma estrutura de herança, mas, a menos que você tenha certeza de que nunca será, aconselho isso. É muito mais fácil traçar a linha do lado cauteloso.
dan_waterworth
2

Existem boas razões para nem sempre usar a inferência de tipo. Haskell tem inferência de tipo, mas geralmente declaro explicitamente os tipos de função. Isso é parcialmente resultado do meu estilo de desenvolvimento. Declaro a função primeiro:

myFunction :: [Int] -> Int
myFunction xs = undefined
Em seguida, vou escrever o código que usa essa função e fazer uma compilação para garantir que digite as verificações. Depois de digitar as verificações, irei adiante com a implementação.

A outra razão para declarar tipos de função é que as declarações podem servir como documentação adicional. Que esta documentação seja verificada em todas as compilações é um bônus.

Larry Coleman
fonte
2
Embora a abordagem do tipo primeiro seja muito elegante em Haskell, duvido que seja adequada para C ++. Sem conceitos , uma assinatura de modelo C ++ basicamente não diz nada - é a implementação que define os tipos e argumentos de requisitos a serem cumpridos. Os modelos simplesmente digitam em tempo de compilação - "tente e veja se funciona". Assim, eu diria que podemos estar implícitos com os tipos, assim como as linguagens dinâmicas tipadas por patos também.
Dario