Pergunta

Estou aprendendo sobre a escrita gratuita e as coisas estavam indo muito bem até que me deparei com isto:

let rec toSeq (reader : SqlDataReader) toItem = seq {
        if reader.Read()
        then
            yield toItem reader
            yield! toSeq reader toItem
        else
            yield! Seq.empty }

e este:

let execute sql init toItem = 
    seq {
        use command = new SqlCommand(sql)
        command |> init
        use connection = new SqlConnection("")
        connection.Open()
        command.Connection <- connection
        use reader = command.ExecuteReader()
        yield! toSeq reader toItem } |> Seq.toList

Não tenho ideia de como passar pelo construtor de sequência ... Isso é mesmo possível?

Preciso ter certeza de que os usos ainda funcionam corretamente também.

Para sua informação: eu sei que pode parecer inútil usar programação sem pontos aqui.Compreenda que este é um exercício de aprendizagem para mim.

ATUALIZAÇÃO: aqui está minha primeira tentativa na segunda função.Tive que remover as referências de sequência:

let ExecuteReader (command : SqlCommand) (connection : SqlConnection) = 
    command.Connection <- connection
    command.ExecuteReader()

let c x y =  ((>>) x) << ((<<) << y)

let (>>|) = c

let execute =
    ExecuteReader 
    >>| ((>>) toSeq) (flip using) 
    >>| using 
    >>| using
Foi útil?

Solução

Bem, como já observamos nos comentários, escrever código imperativo em um estilo sem pontos não é uma boa ideia. Não apenas não o torna mais legível , mas também o torna mais sujeito a erros , porque é mais difícil raciocinar sobre a execução. Mesmo com o código funcional, geralmente acho o estilo não livre de pontos muito mais legível (e não muito mais longo).

De qualquer forma, como você perguntou, aqui estão alguns passos que você pode seguir - observe que este é realmente um exercício de ofuscação funcional. Não é algo que você gostaria de fazer.

O código para a primeira função seria alterado para algo assim (isso será menos eficiente porque as expressões de sequência são otimizadas):

let rec toSeq (reader : SqlDataReader) toItem = Seq.delay (fun () ->
  if reader.Read() then 
    Seq.concat [ Seq.singleton (toItem  reader); toSeq reader toItem ]
  else 
    Seq.empty)

Mesmo no estilo sem pontos, você ainda precisa do Seq.delay para ter certeza de que está executando a sequência lentamente. No entanto, você pode definir uma função ligeiramente diferente que permite escrever código em um estilo mais livre de pontos.

// Returns a delayed sequence generated by passing inputs to 'f'
let delayArgs f args = Seq.delay (fun () -> f args)

let rec toSeq2 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (fun (reader, toItem) ->
    if reader.Read() then 
      Seq.concat [ Seq.singleton (toItem  reader); toSeq2 (reader, toItem) ]
    else 
      Seq.empty)

Agora, o corpo da função é apenas alguma função passada para a função delayArgs. Podemos tentar compor esta função a partir de outras funções em um estilo sem pontos. O if é complicado, por isso o substituímos por um combinador que recebe três funções (e passa a mesma entrada para todas elas):

let cond c t f inp = if c inp then t inp else f inp

let rec toSeq3 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fun (reader, _) -> reader.Read()) 
                  (fun (reader, toItem) -> 
                     Seq.concat [ Seq.singleton (toItem  reader); 
                                  toSeq3 (reader, toItem) ])
                  (fun _ -> Seq.empty))

Você não pode tratar as invocações de membros em um estilo sem pontos, então você precisa definir a função que chama Read. Em seguida, você também pode usar uma função que retorna uma função constante (para evitar colisões de nomes, chamada konst):

let read (reader:SqlDataReader) = reader.Read()
let konst v _ = v

Usando os dois, você pode transformar o último e o segundo argumento em um estilo sem pontos:

let rec toSeq4 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fst >> read) 
                  (fun (reader, toItem) -> 
                     Seq.concat [ Seq.singleton (toItem  reader); 
                                  toSeq4 (reader, toItem) ])
                  (konst Seq.empty))

Usando alguns combinadores mais malucos (uncurry existis em Haskell; o combinador list2 também poderia ser escrito em um estilo sem pontos, mas acho que você entendeu):

let list2 f g inp = List.Cons(f inp, List.Cons(g inp, []))
let uncurry f (a, b) = f a b

let rec toSeq5 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fst >> read) 
                  (list2 ((uncurry (|>)) >> Seq.singleton) toSeq5 >> Seq.concat)
                  (konst Seq.empty))

Isso não é totalmente compilado, porque toSeq5 é avaliado como parte de sua definição, mas se você inserir alguma função de atraso, ela pode realmente fazer a mesma coisa que fez originalmente.

Resumo - Não sei mais se o código acima está correto e como ele avalia (pode avaliar o leitor avidamente, ou conter algum outro tipo de bug). Ele faz a verificação de tipo, então provavelmente não está muito longe de funcionar. O código é completamente ilegível, difícil de depurar e impossível de modificar.

Pense nisso como um exemplo extremo de código louco sem pontos que você pode escrever em F #. Na prática, acho que o estilo sem pontos deve ser usado apenas para coisas triviais, como compor funções usando >>. Depois de precisar definir combinadores como uncurry ou konst, será muito difícil para as pessoas lerem seu código.

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