tuplas sobre pattern multiplicar imprimir funciones funcion drop ciclos basico haskell

sobre - pattern matching haskell



¿Cómo se definen parcialmente las firmas de funciones en Haskell? (6)

Punto de partida:

fn :: [a] -> Int fn = (2 *) . length

Digamos que solo queremos restringir el valor de retorno, entonces podríamos escribir:

fn list = (2 * length list) :: Int

¿Qué tal restringir solo el argumento? Fácil.

fn list = 2 * length (list :: [Char])

Mientras esto funcione, sería preferible que las firmas en la parte superior se recojan y no se diseminen por el cuerpo de la función.

Esto es lo más cerca que podría llegar a esto:

fnSig = undefined :: [Char] -> a fn | False = fnSig | True = (* 2) . length

Basado en http://okmij.org/ftp/Haskell/partial-signatures.lhs través de http://okmij.org/ftp/Haskell/types.html#partial-sigs

Sin embargo, me gustaría una solución más limpia. Algo que comunica mejor que mi intención es la restricción parcial. Algo como esto, por ejemplo:

fn :: [Char] -> a fn = (2 *) . length

O tal vez:

fn :: [Char] -> _ fn = (2 *) . length

es posible?

Editar para mayor aclaración:

@GaneshSittampalam Hizo un punto importante en un comentario a continuación. Estoy buscando "una casa a mitad de camino entre ninguna firma de tipo y tener que dar una precisa". Por lo tanto, no estoy buscando una respuesta basada en TypeClass, solo quiero que GHC complete los espacios para los tipos no especificados (o no totalmente restringidos) de mi función.

Edite en respuesta a @WillNess

Sí, algo como esto ...

fn list = 2 * length list where _ = list :: [Char]

... podría funcionar, pero solo por argumentos, y solo si la función no está libre de puntos. ¿Hay alguna manera de aplicar esta técnica a funciones sin puntos o valores devueltos?

Editar en respuesta a @Rhymoid

Me inspiré y jugué con la idea de @Rhymoid, y se me ocurrió esto:

fn = (2 *) . length where _ = fn `asTypeOf` (undefined :: [Char] -> a) _ = fn `asTypeOf` (undefined :: a -> Int) _ = fn `asTypeOf` (undefined :: a -> b) _ = fn `asTypeOf` (undefined :: a)

Este enfoque también restringe la firma de tipo de fn y no contamina ningún espacio de nombre.

Por lo general, solo tendríamos una de las líneas asTypeOf , solo agregué varias para mostrar cuán poderoso es este enfoque.

Es un poco más torpe que como me gustaría, pero creo que es bastante bueno que podamos hacer esto incluso sin el apoyo sintáctico específico del lenguaje.

@Rhymoid, si te gusta también, por favor agrégalo a tu respuesta. :)


Creo que deseas algo bastante malo. La función que desea aumenta ligeramente la manejabilidad de la inferencia de tipo, especialmente para las funciones de nivel superior. Pero las firmas de declaraciones de alto nivel representan contratos de diseño esenciales. Son API, son documentación, son faros para extraños que inciden en su código, por lo tanto, tienen que ser sólidos y claros.

Sí, haskell permite restricciones de tipo para los tipos de devolución. Pero esto es principalmente para resultados temporales en bloqueos. Y sí, puedes usar

f (x :: Int) = 2*x

sintaxis con extensión XScopedTypeVariables (sin embargo, no es aplicable a funciones sin puntos).


Está buscando una característica que a muchos de nosotros nos gustaría, pero que Haskell no tiene. Ni ghc. Quieres un tipo de firma de tipo parcial. La notación sugerida para esto es

fn :: [Char] -> _ fn = (2*) . length

Donde el _ significa "hay un tipo aquí, pero no me molesto en escribirlo".

Parece una característica muy fácil de implementar (instanciar _ con variables de unificación en la firma), pero nadie se ha molestado en resolver los detalles semánticos y la interacción con otras características.


He estado buscando una manera de decir que el tipo '' x '' se unifica con T ''. Las soluciones dadas por Will Ness y chi están muy cerca de lo que se me ocurrió, pero hay una forma de hacerlo en Haskell 98, sin desbaratar tu propia función.

-- Your function, without type signature. fn = (2 *) . length -- The type signature, without actual definition. fnTy :: [Char] -> a fnTy = undefined -- If this type checks, then the type of ''fn'' can be unified -- with the type of ''fnTy''. fn_unifies_with_type :: () fn_unifies_with_type = let _ = fn `asTypeOf` fnTy in ()

Incluso podrías ir solo

fn = (2 *) . length where _ = fn `asTypeOf` (undefined :: [Char] -> a)


Para especificar solo el tipo de un argumento, puede escribir algo como

fn list = 2 * length list where a :: [Char] a = list `asTypeOf` a

Para que sea fácil modificarlo más tarde como, por ejemplo,

fn list = 2 * fromIntegral (length list) where a :: [Char] a = list `asTypeOf` a

y hacer que su tipo inferido cambie en consecuencia:

*Main> :t fn fn :: [Char] -> Int *Main> :r -- changed file reloaded *Main> :t fn fn :: (Num t) => [Char] -> t

Podría usar la misma técnica contorsionada para especificar el tipo de retorno de una función, tal vez definida en estilo sin pointfree , pero esto no es bonito.

fn2 list = r where r :: Int r = f list f = (2 *) . length

Tampoco es muy diferente de lo que tienes ahora, solo mantiene separados el código y el tipo de especificación.


Perdón por la autopromoción, pero exactamente esta característica es el tema de un artículo reciente de Ph.D. el estudiante Thomas Winant, yo mismo, Frank Piessens y Tom Schrijvers, presentado recientemente por Thomas en el simposio PADL 2014. Vea here para el documento completo. Es una característica que ya está presente en algunos otros idiomas, pero la interacción con funciones como Haskell GADT hizo que fuera lo suficientemente interesante como para resolver los detalles.

Thomas está trabajando en una implementación para GHC. Ha mejorado aún más desde la redacción del documento, pero implementar la "restricción de comodín" en GHC es técnicamente un poco más difícil de lo que esperábamos. Esperamos poder trabajar más allá y contactar a los desarrolladores de GHC para que sea adoptado, pero el hecho de que esto suceda dependerá de la cantidad de personas que deseen tener la función en Haskell ...

Actualización 14-4-2015: después de un montón de trabajo por parte de Thomas y la contribución de SPJ y otras personas de GHC, se han lanzado firmas de tipo parciales en GHC 7.10. Thomas Winant escribió una publicación introductoria sobre cómo puede usarlos.


Si el tipo de su fn se puede inferir automáticamente sin una firma, y ​​simplemente desea que el compilador verifique si el tipo inferido tiene la forma correcta, puede usar algo a lo largo de lo siguiente.

La idea es escribir algo como

fnSig :: exists _1 _2. forall a. _1 a -> _2 fnSig = fn

excepto que Haskell no permite los tipos existenciales arriba. Sin embargo, los tipos existenciales se pueden emular utilizando tipos de rango superior de la siguiente manera:

{-# LANGUAGE RankNTypes #-} fnSig :: (forall _1 _2. (forall a. _1 a -> _2) -- your actual type, _''s are the unknowns ->r)->r fnSig = /k->k fn -- the compiler infers _1=[] , _2=Int -- fn :: [] a -> Int fn = (2 *) . length

El "truco" anterior es esencialmente el mismo que el utilizado en, por ejemplo, runST.

Alternativamente, uno podría declarar un tipo de datos existencial ad-hoc.

{-# LANGUAGE GADTs #-} data Ex where Ex :: (forall a. _1 a -> _2) -> Ex fnSig = Ex fn

que debería obligar al compilador a realizar la misma verificación de tipo.