No Haskell, posso usar o tipo a -> Maybe b
para modelar uma função que retorna um valor do tipo b
ou não retorna nada (falha).
Se eu tiver tipos a1, ..., a(n+1)
e funções f1, ..., fn
, com fi :: ai -> Maybe a(i+1)
para todos i
, 1 <= i <= n
posso encadear as funções usando o >>=
operador da Maybe
mônada e escrever:
f1 x >>= f2 >>= f3 >>=... >>= fn
O >>=
operador garante que cada função seja aplicada desde que seu predecessor retorne um valor significativo. Assim que uma função na cadeia falha, a cadeia inteira falha (retorna Nothing
) e outras funções na cadeia não são avaliadas.
Eu tenho um padrão um pouco semelhante no qual desejo tentar várias funções na mesma entrada e retornar assim que uma função for bem-sucedida . Se todas as funções falharem (retornar Nothing
), todo o cálculo falhará. Mais precisamente, tenho funções f1, ..., fn :: a -> Maybe b
e defino a função
tryFunctions :: [a -> Maybe b] -> a -> Maybe b
tryFunctions [] _ = Nothing
tryFunctions (f : fs) x = case f x of
Nothing -> tryFunctions fs x
r@(Just _) -> r
Em certo sentido, isso é duplo para a Maybe
mônada, pois um cálculo para no primeiro sucesso, e não na primeira falha.
Obviamente, posso usar a função que escrevi acima, mas fiquei pensando se há uma maneira melhor, bem estabelecida e idiomática de expressar esse padrão em Haskell.
return f1 ?? f2 ?? f3 ?? DefaultValue;
Alternative
que é o operador símbolo infix<|>
e é definido em termos de uma MonoidRespostas:
Dado um conjunto fechado (número fixo de elementos)
S
com elementos{a..z}
e um operador binário*
:Existe um único elemento de identidade
i
que:forall x in S: i * x = x = x * i
O operador é associativo de modo que:
forall a, b, c in S: a * (b * c) = (a * b) * c
Você tem um monóide.
Agora, dado qualquer monóide, você pode definir uma função binária
f
como:O que isso significa é que, para o exemplo do
Maybe
monóide (Nothing
é o elemento de identidade indicado acima comoi
):Surpreendentemente, não consigo encontrar essa função precisa nas bibliotecas padrão, o que provavelmente se deve à minha própria inexperiência. Se mais alguém puder oferecer isso voluntariamente, eu agradeceria sinceramente.
Aqui está a implementação que deduzi de imediato do exemplo acima:
Exemplo:
Então, se você quiser usar uma lista de funções como entrada ...
exemplo:
fonte
<|>
trata a identidade de um monóide de uma maneira especial. Não se pode escolher um elemento arbitrário de um conjunto arbitrário para desempenhar esse papel especial? Por que o conjunto precisa ser um monóide e o elemento especial cria<|>
sua identidade?<|>
que não confie no monóide e eu tenho tudo isso misturado? Baseia-se naAlternative
classe. Para ter certeza - eu estou olhando para a minha própria resposta e perceber que não é muito bem como[1,2] <|> [3]
dá o inesperado[1,2,3]
então tudo sobre como utilizar a classe tipo monoid para identificar uma identidade é certo - e a outra chave é associatividade é necessário para obter o comportamento esperado , talvezAlternative
não lhe dá o comportamento que eu pensava fora da mão ...f
para ver por que associatividade é uma necessidade.fonte
Maybe
... Minha solução é limitada porEq
, de alguma forma eu sinto que nós dois estamos perdendo algo disponível por meio deMonoid
geral ...either
?Monoid
a de que qualquer coisa com um elemento de identidade possa ter um conjunto de 2 funções (acho que esse é um tipo de campo binário), onde uma das funções escolhe a identidade sobre todas as outras, e a outra função sempre escolhe o elemento não identitário. Eu simplesmente não sei como fazer isso semEq
saber qual é o valoridentity
ou não .. obviamente, em monoides aditivos ou multiplicativos, você obtém a função ladder por padrão (sempre escolha elementos não identitários usando a função binária de monoides)foldMap
pode ser usado no lugar demconcat . map
Isso parece muito com a substituição de falhas por uma lista de sucessos
Você está falando mais
Maybe a
do que[a]
, mas na verdade eles são muito parecidos: podemos pensarMaybe a
como sendo[a]
, exceto que pode conter no máximo um elemento (ie.Nothing ~= []
EJust x ~= [x]
).No caso de listas, você
tryFunctions
seria muito simples: aplique todas as funções ao argumento fornecido e concatene todos os resultados juntos.concatMap
fará isso muito bem:Dessa maneira, podemos ver que o
<|>
operadorMaybe
atua como concatenação para 'listas com no máximo um elemento'.fonte
No espírito de Conal, divida-o em operações menores e mais simples.
Neste caso,
asum
deData.Foldable
faz a parte principal.Como alternativa, na resposta de Jimmy Hoffa, você pode usar a
Monoid
instância,(->)
mas precisa de umaMonoid
instânciaMaybe
e a padrão não faz o que você deseja. Você querFirst
deData.Monoid
.(Ou
mconcat
para uma versão mais antiga e especializada defold
.)fonte