Refiriéndose a la excelente respuesta de jbapple con respecto a replicate, pero usando replicateA(que replicatese basa en) en su lugar, se me ocurrió lo siguiente:
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList(en una versión ligeramente más eficiente) ya está definido y utilizado internamente en Data.Sequencepara la construcción de árboles de los dedos que son resultados de las clases.
En general, la intuición para replicateAes simple. replicateAestá construido encima de la función applytiveTree . applicativeTreetoma un pedazo de un árbol de un tamaño my produce un árbol bien equilibrado que contiene ncopias de este. Las cajas para nhasta 8 (un solo Deepdedo) están codificadas. Cualquier cosa por encima de esto, y se invoca recursivamente. El elemento "aplicativo" es simplemente que intercala la construcción del árbol con efectos de enhebrado, como, en el caso del código anterior, el estado.
La gofunción, que se replica, es simplemente una acción que obtiene el estado actual, saca un elemento de la parte superior y reemplaza el resto. En cada invocación, por lo tanto, baja más abajo en la lista proporcionada como entrada.
Algunas notas más concretas
main = print (length (show (Seq.fromList [1..10000000::Int])))
En algunas pruebas simples, esto produjo una compensación de rendimiento interesante. La función principal anterior se ejecutó casi 1/3 más bajo con myFromList que con fromList. Por otro lado, myFromListusó un montón constante de 2 MB, mientras que el estándar fromListusó hasta 926 MB. Ese 926 MB surge de la necesidad de mantener toda la lista en la memoria a la vez. Mientras tanto, la solución myFromListes capaz de consumir la estructura de forma perezosa. El problema con la velocidad resulta del hecho de que myFromListdebe realizar aproximadamente el doble de asignaciones (como resultado de la construcción / destrucción del par de la mónada estatal) quefromList. Podemos eliminar esas asignaciones moviéndonos a una mónada de estado transformada por CPS, pero eso da como resultado retener mucha más memoria en un momento dado, porque la pérdida de la pereza requiere atravesar la lista de manera no continua.
Por otro lado, si en lugar de forzar la secuencia completa con un espectáculo, me muevo a solo extraer la cabeza o el último elemento, myFromListinmediatamente presenta una ganancia mayor: extraer el elemento de la cabeza es casi instantáneo y extraer el último elemento es 0.8s . Mientras tanto, con el estándar fromList, extraer la cabeza o el último elemento cuesta ~ 2.3 segundos.
Todo esto son detalles, y es consecuencia de la pureza y la pereza. En una situación con mutación y acceso aleatorio, me imagino que la replicatesolución es estrictamente mejor.
Sin embargo, plantea la cuestión de si hay una manera de reescribir applicativeTreetal que myFromListsea estrictamente más eficiente. Creo que el problema es que las acciones aplicativas se ejecutan en un orden diferente al que se atraviesa naturalmente el árbol, pero no he trabajado completamente sobre cómo funciona esto, o si hay una manera de resolverlo.