Por favor, confirmar ou corrigir o meu "inglês interpretação" de Haskell este trecho de código

StackOverflow https://stackoverflow.com/questions/775730

Pergunta

Eu sou um C# desenvolvedor que está trabalhando através de "Mundo Real Haskell" a fim de compreender verdadeiramente de programação funcional, de modo que quando eu aprender F# eu vou realmente grok, e não apenas "escrever código C# em F#", por assim dizer.

Bem, hoje me deparei com um exemplo que eu pensei que eu entendi 3 momentos diferentes, para só depois ver algo que eu perdi, atualizar a minha interpretação, e recurse (e xingar muito, acredite em mim).

Agora eu acredito que eu realmente compreendê-lo, e eu escrevi um detalhado "em inglês interpretação" abaixo.Você pode Haskell gurus por favor, confirme que a compreensão, ou apontar o que eu perdi?

Nota:O Haskell trecho de código (citado diretamente do livro) é definir um tipo que está destinado a ser isomórfica à construído em Haskell tipo de lista.

O trecho de código Haskell

data List a = Cons a (List a)
              | Nil
              defining Show

EDITAR:Depois de algumas respostas, eu vejo um mal-entendido que eu fiz, mas não sou muito claro sobre o Asclock, uma "análise" regras que iria corrigir esse erro.Então eu incluí meu original (incorreta) a interpretação abaixo, seguido de uma correção, seguido por a questão que ainda permanece pouco claro para mim.

EDITAR:Aqui é a minha original (incorreto) "em inglês interpretação" do trecho de

  1. Eu sou a definição de um tipo chamado "Lista".
  2. O tipo de Lista é parametrizada.Ele tem um único parâmetro de tipo.
  3. Há 2 valor de construtores que pode ser usado para criar instâncias de Lista.Um construtor de valor é chamado de "Nulo" e o outro valor construtor é chamado de "Contras".
  4. Se você usar o "Nil" construtor de valor, então não há campos.
  5. Os "Contras" construtor de valor tem um único parâmetro de tipo.
  6. Se você usar os "Contras" construtor de valor, existem 2 campos que devem ser fornecidos.O primeiro campo é uma instância de Lista.O segundo campo é uma instância de um.
  7. (Eu tenho intencionalmente omitida nada sobre "definição de Mostrar" porque ele não é parte do que eu quero me concentrar agora).

Corrigida a interpretação seria a seguinte (alterações em NEGRITO)

  1. Eu sou a definição de um tipo chamado "Lista".
  2. O tipo de Lista é parametrizada.Ele tem um único parâmetro de tipo.
  3. Há 2 valor de construtores o que pode ser usado para criar instâncias da Lista.Um construtor de valor é chamado de "Nulo" e o outro valor construtor é chamado de "Contras".
  4. Se você usar o "Nil" construtor de valor, então não há campos.

    5.(esta linha foi excluída ...não é preciso) Os "Contras" construtor de valor tem um único parâmetro de tipo.

  5. Se você usar os "Contras" construtor de valor, existem 2 campos o que deve ser fornecida.O primeiro campo obrigatório é uma instância de um.O segundo campo é uma exemplo de "Lista-de-um".

  6. (Eu tenho intencionalmente omitida nada sobre "definição de Mostrar" porque ele não é parte do que eu quero me concentrar agora).

A questão que ainda não está claro

A confusão inicial foi sobre a parte do trecho em que se lê "Contras de um (Lista a)".Na verdade, essa é a parte que ainda não está claro para mim.

As pessoas têm apontado que cada item na linha após o "Contras" token é um tipo, não um valor.Então, o que significa que esta linha diz que "Os Contras valor construtor tem 2 campos:um do tipo 'a' e o outro do tipo "lista-de-um'."

Que é muito útil saber.No entanto, algo ainda não é claro.Quando eu criar instâncias usando os Contras do construtor de valores de, nesses casos, "interpretar" o primeiro 'a' com o significado de "colocar o valor passado aqui." Mas eles não não interpretar o segundo 'a', da mesma forma.

Por exemplo, considere este GHCI sessão:

*Main> Cons 0 Nil
Cons 0 Nil
*Main> Cons 1 it
Cons 1 (Cons 0 Nil)
*Main> 

Quando eu tipo "Contras 0 Nil", ele usa os "Contras" valor construtor para criar uma instância de Lista.A partir de 0, ele descobre que o tipo de parâmetro é "Inteiro".Até agora, nenhuma confusão.

No entanto, ele também determina que o valor do primeiro campo dos Contras é 0.Ainda assim, determina nada sobre o valor o segundo campo ...ele apenas determina que o segundo campo tem um tipo da Lista de "Inteiro".

Então a minha pergunta é, por que não "um" no primeiro campo significa "o tipo deste campo é 'um' e o valor deste campo é 'a'", enquanto "a", no segundo campo do meio apenas "o tipo deste campo é 'Lista de'"?

EDITAR:Eu acredito que eu já vi a luz, graças a várias das respostas.Deixe-me expressar aqui.(E se, de alguma forma, é ainda incorreto de alguma forma, por favor, por todos os meios me avise!)

No trecho "Contras de um (Lista a)", estamos a dizer que os "Contras" construtor de valor tem dois campos, e que o primeiro campo é do tipo 'a', e que o segundo campo é do tipo 'Lista de'.

Que é tudo o que estamos dizendo! Em particular, estamos dizendo: NADA sobre valores!Esse é um ponto chave que eu estava ausente.

Mais tarde, queremos criar uma instância, usando os "Contras" construtor de valor.Nós digite isso para o interpretador:"Contras 0 Nil".Este explicitamente informa os Contras do construtor de valores de usar 0 para o valor do primeiro campo, e para usar Nil como o valor para o segundo campo.

E isso é tudo que existe para ela.Uma vez que você sabe que o construtor de valor de definição especifica mas nada de tipos, tudo se torna claro.

Obrigado a todos pela útil respostas.E como eu disse, se alguma coisa ainda está off, por favor, por todos os meios conte-me sobre ele.Obrigado.

Foi útil?

Solução

  • Os "Contras" construtor de valor tem um único parâmetro de tipo.

Nope:você já parametrizando-lo quando declarada data List a.Um eficaz propriedade deste é que se eu tiver uma Nil ::Lista de Int, eu não posso intercâmbio com um Nil ::Lista De Char.

  • Se você usar os "Contras" construtor de valor, existem 2 campos que devem ser fornecidos.O primeiro campo é uma instância de Lista.O segundo campo é uma instância de um.

Tenho trocado:o primeiro campo é uma instância de um, o segundo campo é uma instância de Lista.

Este capítulo do Mundo Real Haskell podem ser do seu interesse.

Obrigado.Que é o capítulo que eu estou agora.Então ...quando o código diz que "os Contras de um (Lista a)", eu pensei que os "Contras" uma parte do que estava afirmando que os Contras valor construtor foi parametrizada.Eles ainda não cobertos a sintaxe para parametrizado, tipos, então eu imaginei que a sintaxe deve exigir re-afirmando que "um" se você pretende usar um.Mas você está dizendo que não é necessário?E, portanto, não o que "a" significa?

Não.Uma vez que nós declaramos um parâmetro no nosso tipo, podemos reutilizá-lo de outra forma de dizer "que tipo deve ser usado lá." É um pouco como uma a -> b -> a tipo de assinatura:uma é a parametrização do tipo, mas então eu tenho que usar o mesmo como o valor de retorno.

OK, mas isso é confuso.Parece que o primeiro "a" significa "o primeiro campo é uma instância de um",

Não, o que é não verdade.Apenas significa que o tipo de dados parametrizes sobre algum tipo de um.

e TAMBÉM significa "o primeiro campo tem o mesmo valor como o valor que eles passaram por um".Em outras palavras, ele especifica o tipo E o valor.

Não, isso também não é verdade.

Aqui está um bom exemplo, a sintaxe do que você pode ou não pode ter visto antes:

foo :: Num a => a -> a

Este é um padrão bastante assinatura para uma função que recebe um número e faz alguma coisa para ele e dá-lhe outro número.O que eu realmente quero dizer por "um número" em Haskell-falar, apesar de que, é algum tipo arbitrário "um" que implementa o "Número" de classe.

Assim, este analisa para o inglês:

Deixe um indicar um tipo de execução Num typeclass, em seguida, a assinatura deste método é um parâmetro com o tipo a, e o valor de retorno de um tipo de

Algo semelhante acontece com os dados.

Ele também me ocorre que a instância de Lista na especificação dos Contras é também confuso você:ter muito cuidado ao analisar que:considerando que os Contras é a especificação de um construtor, que é, basicamente, um padrão que Haskell está indo para encapsular os dados em, (Lista a) parece um construtor, mas, na verdade, é simplesmente um tipo, como Int ou Double.um é um tipo, NÃO é um valor em qualquer sentido do termo.

Editar: Em resposta a edição mais recente.

Eu acho que uma dissecção primeiro é chamado para.Então eu vou lidar com suas dúvidas ponto por ponto.

Haskell dados construtores são um pouco estranho, porque você definir o construtor de assinatura, e você não tem que fazer qualquer outra andaime.Tipos de dados em Haskell não tem qualquer noção de variável de membro.(Nota:há uma sintaxe alternativa que esta maneira de pensar é mais passível, mas vamos ignorar que, por enquanto).

Outra coisa é que o código Haskell é densa;seu tipo de assinatura são como que para.Então, esperamos para ver se o mesmo símbolo reutilizados em diferentes contextos.Tipo inferencing também desempenha um grande papel aqui.

Então, de volta para o seu tipo:

data List a = Cons a (List a)
              | Nil

Eu trecho presente em várias peças:

data Lista

Isso define o nome do tipo, e qualquer tipos parametrizados, que terá mais tarde.Note que você só vai ver esse show em outro tipo de assinaturas.

Contras a (List a) |
Nil

Este é o nome de dados do construtor. Este NÃO É um tipo de.Podemos, no entanto, o padrão de correspondência para ele, ala:

foo :: List a -> Bool
foo Nil = True

Observe como a Lista é o tipo de assinatura, e nada é tanto o de dados construtor e a "coisa" nós correspondência de padrão para.

Cons um (Lista a)

Estes são os tipos de valores que, slot para o construtor.Contras tem duas entradas, uma é do tipo um, e um é do tipo Lista.

Então a minha pergunta é, por que não "um" no primeiro campo significa "o tipo deste campo é 'um' e o valor deste campo é 'a'", enquanto "a" no segundo campo significa apenas "o tipo deste campo é 'Lista de'"?

Simples:não pense nisso como nós, especificando o tipo;acho que isso tem Haskell é inferencing o tipo de fora.Assim, para nossos propósitos, nós simplesmente estamos enfiando um 0 ali, e um Nulo na segunda seção.Em seguida, Haskell olha para o nosso código e pensa:

  • Hmm, gostaria de saber qual o tipo de Contras 0 Nil é
  • Bem, Contras é um construtor para a Lista um.Gostaria de saber qual o tipo de Lista é um
  • Bem, um é usado no primeiro parâmetro, portanto, desde o primeiro parâmetro é um Int (outra simplificação;0 é realmente uma coisa estranha que é typeclassed como Num), que significa um é um Núm
  • Hey, bem, isso também significa que o tipo de Nil é a Lista de Int, apesar de não há nada lá que poderia realmente dizer que

(Note que não é, na verdade, a forma como é implementado.Haskell pode fazer um monte de coisas estranhas, enquanto inferencing tipos, o que é parcialmente porque as mensagens de erro chupar.)

Outras dicas

Analogias são geralmente falta, em todas as formas, mas desde que você saiba C# eu pensei que isso poderia ser útil.

Isto é como eu descreveria o List a definição em C#, talvez isso afasta algumas coisas (ou, mais provavelmente, confunde ainda mais).

class List<A>
{
}

class Nil<A> : List<A>
{
    public Nil() {}
}

class Cons<A> : List<A>
{
    public A Head;
    public List<A> Tail;

    public Cons(A head, List<A> tail)
    {
        this.Head = head;
        this.Tail = tail;
    }
}

Como você pode ver;

  • o List tipo tem um único parâmetro de tipo (<A>),
  • o Nil construtor não tem parâmetros,
  • e o Cons construtor tem dois parâmetros, um valor head do tipo A e um valor tail do tipo List<A>.

Agora, em Haskell o Nil e Cons são apenas construtores para a List a tipo de dados em C# eles também são tipos de em e de si mesmos, de modo que é onde a analogia falha.

Mas eu espero que isso dá sensação intuitiva de que as diferentes A's representam.

(E por favor, faça um comentário sobre como este horrível comparação não faz justiça ao Haskell os tipos de dados.)

Cons a (List a)

O primeiro campo de um Cons é um valor do tipo "a".O segundo é um valor do tipo "List a", i.é.uma Lista parametrizada com o mesmo tipo do parâmetro da lista atual.

5 é errado, e eu diria que 6 da seguinte forma para substituir:

Contras{1} um{2} (Lista a){3} é um construtor chamado Contras (parte antes de {1}) para um valor de tipo de Lista (a Lista de dados de uma parte) que precisa de dois valores:um do tipo um (a parte entre {1} e {2}) e um tipo de Lista de um(a parte entre {2} e {3}).

Para ajudar você com uma aparente fonte de confusão:em Haskell, você quase nunca tem que dar explícita de parâmetros de tipo de tipo de inferência infere os tipos de valores.Assim, em um sentido, sim, quando você passar um valor para uma função ou construtor, você também deve especificar um tipo, a saber.o tipo do valor passado.

Sim, os dados de sintaxe é um pouco confuso, porque ele trocadilhos de nomes e tipos, e não é realmente sintática distinção entre eles.Em particular, em um construtor definição de como:

Cons a (List a)

A primeira palavra é o nome do construtor;todos os outros word é o nome de alguns predeclared tipo.Assim, tanto a e List a já estão no escopo (o a foi trazido para o âmbito, o a no "data List a"), e você está dizendo que esses são os tipos dos parâmetros.Seu papel pode ser melhor demonstrada pela afirmando a mesma coisa usando registro de sintaxe:

Cons { headL :: a, tailL :: List a }

I. e.um valor do tipo List Int, se ele foi construído com o Cons construtor, tem dois campos:um Int e um List Int.Se ele foi construído com Nil, ele não tem nenhum campos.

Quando eu digito "Cons 0 Nil", ele usa o "Cons"valor construtor para criar uma instância de Lista.A partir de 0, ele descobre que o tipo de parâmetro é "Integer".Até agora, nenhuma confusão.

No entanto, ele também determina que o valor do primeiro campo dos Contras é 0.Ainda assim, ele determina nada sobre o valor do segundo campo ...ele apenas determina que o segundo campo tem um tipo de "Lista Inteiro".

Não, ele determina que o segundo valor do campo é Nil.Dada a sua definição, Nil é um valor do tipo List a.Por isso, é Cons 0 Nil.E no Cons 1 it o segundo valor do campo é it;por exemplo, Cons 0 Nil.Este é precisamente o que o REPL mostra:Cons 1 (Cons 0 Nil).

Olhei para o seu editada questão.

Quando eu criar instâncias usando os Contras construtor de valor, essas instâncias "interpretar" o primeiro 'a' como significado "coloque o valor passado aqui.

Em "Contras de um (Lista a)", "um" e "a Lista" são tipos.Eu não entendo o que "valor" tem para com ele.

Quando eu tipo "Contras 0 Nil", ele usa o "Contras" valor construtor para criar uma exemplo de Lista.A partir de 0, aprende que o tipo de parâmetro é "Inteiro".Até agora, nenhuma confusão.

No entanto, ele também determina que o o valor do primeiro campo da Contras é 0.Ainda assim, ele determina nada sobre o valor do segundo campo ...ele apenas determina que o segundo campo tem um tipo de "Lista Inteiro".

O valor do segundo campo é Nil.

Então a minha pergunta é, por que não "um" no primeiro campo significa "o tipo de campo de 'um' e o valor desta campo de 'um'", enquanto "um" na segunda campo significa apenas "o tipo de o campo é a 'Lista de'"?

"um" no primeiro campo, que significa "o tipo deste campo é 'um'"."A lista", no segundo campo, que significa "o tipo deste campo é 'Lista'".No caso dos "Contras 0 Nil" acima, 'a' é inferida a ser "Integer".Assim, "Contras de um (Lista a)" torna-se "Contras Inteiro (Lista de Inteiros)".O 0 é um valor do tipo Inteiro.O zero é um valor do tipo "Lista de Inteiros".

o valor deste campo é 'a'

Eu não entendo o que você quer dizer com isso."a" é uma variável do tipo;o que isso tem a ver com valores?

Só para dar alguns extras "ajuda", no caso de você ainda está assistindo esta thread.Haskell tem um casal de convenções que atrapalhar os outros " ideias de como as coisas deveriam ser feitas em Haskell, um tipo parametrizado é tão comumente aceito, que é geralmente pensada como sendo um tipo de função de nível.Da mesma forma, o valor de construtores são pensados como "especial" de funções, que também permite a correspondência de padrões, além de sua "tomar um valor (ou mais) e produzir um valor como resultado".

Outro "engraçado" característica do Haskell é que ele não explicitamente (ou implicitamente) avaliar os argumentos para uma função, mesmo que o argumento está em parênteses.Deixe-me dizer que de forma ligeiramente diferente:Linux não avaliar argumentos entre parênteses antes de outros argumentos.Os argumentos são colocados entre parênteses para agrupamento meramente informativo, não tê-las avaliadas "em primeiro lugar".Haskell atribui argumentos ("aplica-se") uma função em uma precedência mais alta do que qualquer outra operação - até mais do que implícita uma aplicação de função de um dos seus próprios argumentos.É por isso que o Cons construtor tem parens todo o segundo argumento, (List a) - para dizer ao compilador que Cons tem dois argumentos, e não três.Os parênteses para agrupar apenas, e não para de precedência!

Como um pouco de um lado tópico, tenha cuidado com os tipos em F#.Desde que F# tem suas raízes no ML, seus tipos parametrizados ter parâmetros na frente - int list, não (List Int) de volta!Haskell faz-lo de outra maneira, porque é a mesma forma que Haskell faz funções - primeira função, em seguida, argumentos para a função.Isso estimula um padrão de uso comum, e explica o porquê de tipos de Haskell e valor construtores são Capitalizados - para lembrá-lo de que você está lidando com um tipo/classe relacionadas com a coisa.

Ok, eu sou feito;obrigado por me deixar colocar esse enorme Parede O' Texto em sua propriedade...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top