Estou tentando fazer esta pergunta do meu dever de casa:
Dado arbitrário
foo :: [[a]] -> ([a], [a])
, escreva uma lei que a funçãofoo
satisfaça, envolvendomap
listas e pares.
Algum contexto: Eu sou o primeiro ano de graduação fazendo um curso de programação funcional. Embora o curso seja bastante introdutório, o palestrante mencionou muitas coisas dos conteúdos programáticos, entre as quais os teoremas livres. Então, depois de tentar ler o artigo de Wadler, deduzi que, concat :: [[a]] -> [a]
com a lei, map f . concat = concat . map (map f)
parece relevante para o meu problema, já que precisamos ter foo xss = (concat xss, concat' xss)
onde concat
e onde concat'
houver funções do tipo [[a]] -> [a]
. Então foo
satisfaz bimap (map f, map g) . foo = \xss -> ((fst . foo . map (map f)) xss, (snd . foo . map (map g)) xss)
.
Essa "lei" já parece longa demais para ser correta, e também não tenho certeza da minha lógica. Então, pensei em usar um gerador de teoremas gratuitos on-line , mas não entendi o que lift{(,)}
significa:
forall t1,t2 in TYPES, g :: t1 -> t2.
forall x :: [[t1]].
(f x, f (map (map g) x)) in lift{(,)}(map g,map g)
lift{(,)}(map g,map g)
= {((x1, x2), (y1, y2)) | (map g x1 = y1) && (map g x2 = y2)}
Como devo entender essa saída? E como devo derivar a lei para a função foo
corretamente?
fonte
(\(a,b) -> (map f a, map f b)) . foo = foo . map (map f)
Respostas:
Se
R1
eR2
são relações (digamos,R_i
entreA_i
eB_i
comi in {1,2}
), entãolift{(,)}(R1,R2)
são os pares de relações "elevados", entreA1 * A2
eB1 * B2
com a*
designação do produto (escrito(,)
em Haskell).Na relação levantada, dois pares
(x1,x2) :: A1*A2
e(y1,y2) :: B1*B2
são relacionados se e somente sex1 R1 y1
ex2 R2 y2
. No seu caso,R1
eR2
são funçõesmap g, map g
, de modo que o levantamento se torna uma função, bem como:y1 = map g x1 && y2 = map g x2
.Portanto, o gerado
significa:
ou, em outras palavras:
que eu escreveria como, usando
Control.Arrow
:ou mesmo, no estilo pointfree:
Isso não é surpresa, pois você
f
pode ser escrito comoe
F
,G
são functors (em Haskell seria preciso usar umnewtype
para definir uma instância functor, mas vou omitir que, uma vez que é irrelevante). Nesse caso comum, o teorema livre tem uma forma muito agradável: para cadag
,Essa é uma forma muito agradável, chamada naturalidade (
f
pode ser interpretada como uma transformação natural em uma categoria adequada). Observe que os doisf
s acima são realmente instanciados em tipos separados, para fazer com que os tipos concordem com o restante.No seu caso específico, uma vez
F a = [[a]]
que é a composição do[]
functor , ele é o que obtemos (sem surpresa)fmap_of_F g = fmap_of_[] (fmap_of_[] g) = map (map g)
.Em vez disso,
G a = ([a],[a])
é a composição dos functores[]
eH a = (a,a)
(tecnicamente, o diagonal é composto com o produto). Nós temosfmap_of_H h = (h *** h) = (\x -> (h x, h x))
, a partir do qualfmap_of_G g = fmap_of_H (fmap_of_[] g) = (map g *** map g)
.fonte
g
total. Da mesma forma, como temosseq
, precisamos exigirg
que seja rigoroso. Não tenho 100% de certeza sobre as restrições exatas, mas acho que devem ser essas. Mas não me lembro onde li sobre isso - provavelmente na página do gerador de teoremas livres, há algumas informações.map
vsfmap
. As pessoas continuam usando,map
pois torna óbvio que estamos lidando com listas (e não com outro functor). Da mesma forma,(***)
só funciona em pares (e não em outros bifuncionais). Provavelmente o estou usando principalmente para o seu infix-ness, já que em matemática tendemos a escreverf \times g
para aplicar o bifunctor do produto. Talvezbimap
deva ter sua variante infix também, como<$>
é uma variante parafmap
.(***)
seja mais específico dobimap
que apenas trabalhando em pares, em vez de bifuncionais arbitrários, também é verdade quebimap
é mais específico do(***)
que apenas trabalhando em funções e não em setas arbitrárias. Re infix, isso não seria o mesmobimap
efmap
, já quebimap
leva 3 parâmetros efmap
leva apenas 2. #A mesma coisa que a resposta de @ chi com menos cerimônia:
Não importa se você muda
a
s parab
s antes ou depois da função, você obtém a mesma coisa (desde que use umafmap
coisa parecida para fazer isso).fonte