2011-02-07 8 views
10

He estudiado el lenguaje de programación Haskell y ahora descubrí que es posible llamar a las funciones de Haskell desde los programas C. Durante mis estudios en Haskell, creé un contador de frecuencia de palabras con Haskell y me gustaría intentar llamar a esa función desde un programa en C, pero no sé cómo hacerlo. He encontrado estos dos sitios web en haskell.org:¿Cómo elegir el tipo de Haskell C correcto?

Calling Haskell from C

Foreign C types (Haskell module)

A pesar de eso, estoy un poco perdido los tipos de usar. Mis programas haskell es una lista de las siguientes funciones:

putStr. unlines. mapa testF. sortedTree

donde mis propias funciones

  • testF es el tipo de testF :: Show a => ([Char], a) -> [Char]
  • sortedTree es el tipo de sortedTree :: (Num a, Ord a) => [Char] -> [([Char], a)]

Estoy bastante seguro de que necesito convertir los tipos de cada función a tipos C, en lugar de convertir solo la función que llama a la tubería. El tipo de la función "principal" es

fileFreq :: [Char] -> IO()

Además de todo esto, estoy usando un árbol binario Haskell, que no es el tipo preludio.

Aquí es todo el código Haskell:

module WordCounter where 

import List 
import Char 
import Foreign.C.Types 

data BTree a = Tip | BNode a (BTree a) (BTree a) deriving Show 

insertFreq x Tip = BNode (x,1) Tip Tip 
insertFreq x (BNode (q,p) l r) | (map toLower x)==(map toLower q) = BNode (q, p+1) l r 
       | otherwise     = BNode (q,p) l (insertFreq x r) 

tlist :: BTree a -> [a] 
tlist Tip = [] 
tlist (BNode x l r) = concat [tlist l, [x], tlist r] 

sortedTree x = sortBy (\(x,y) (p,q) -> compare q y) (tlist (foldr insertFreq Tip (words x))) 

testF (x, n) = concat (x : ":" : " \t\t\t " : show n : []) 

concord = putStr . unlines . map testF . sortedTree 

fileFreq filename = do { text <- readFile filename; concord text } 

Puede alguien guiarme un poco con esto?

+5

pregunta frío, por desgracia nadie en esta matriz parece ser útiles http://asset.soup.io/asset/0750/2820_15d5_960.jpeg espero que no es ofensivo, solo divertido mientras esperas una respuesta razonable. +1 por supuesto – stacker

+0

Esto no está claro para mí. ¿Podría ser explícito sobre qué función Haskell desea llamar desde C? Imagina que tienes las ataduras funcionando, ¿cómo se ve una simple llamada C? –

+1

@stacker: No tiene precio: D – Skurmedel

Respuesta

7

Lo que tendrá que hacer es crear funciones de envoltura para las funciones que necesita exponer a C y hacer el trabajo de convertir de tipos C a tipos de haskell.

También necesitará habilitar la extensión ForeignFunctionInterface, también cualquier excepción que pueda ocurrir en el código haskell debe manejarse en las funciones del contenedor.

Por ejemplo, si sólo necesita exponer su función fileFreq de nivel superior para C Se podría añadir una función como:

fileFreq_hs :: CString -> IO CInt 
fileFreq_hs cstr = catch (wrap_fileFreq cstr) (\_ -> return (-1)) 
    where wrap_fileFreq = do 
      str <- peekCString cstr 
      fileFreq str 
      return 0 

para crear una función que reúne un C-secuencia en una cadena de Haskell (utilizando funciones de Foreign.C.String), llama a su función fileFreq y traduce excepciones a C códigos de error (-1 si ocurrió una excepción, 0 en caso contrario).

Luego hay que exportarlo usando

foreign export ccall fileFreq_hs :: CString -> IO CInt 

y por supuesto es necesario agregar:

{-# LANGUAGE ForeignFunctionInterface #-} 

en la parte superior de su módulo.

Luego puede seguir las instrucciones en los enlaces que proporcionó para compilar esto en un C-stub y un archivo de encabezado y crear un archivo C que puede compilar con ghc.

Por supuesto, es posible ajustar cualquier función que tenga, solo necesita asegurarse de manejar posibles excepciones y organizar entre tipos C y tipos de Haskell.

El código completo con mis modificaciones es:

{-# LANGUAGE ForeignFunctionInterface #-} 
module WordCounter where 

import List 
import Char 
import Foreign.C.Types 
import Foreign.C.String 
import Control.Monad 

data BTree a = Tip | BNode a (BTree a) (BTree a) deriving Show 

insertFreq x Tip = BNode (x,1) Tip Tip 
insertFreq x (BNode (q,p) l r) | (map toLower x)==(map toLower q) = BNode (q, p+1) l r 
       | otherwise     = BNode (q,p) l (insertFreq x r) 

tlist :: BTree a -> [a] 
tlist Tip = [] 
tlist (BNode x l r) = concat [tlist l, [x], tlist r] 

sortedTree :: (Ord t, Num t) => String -> [([Char], t)] 
sortedTree x = sortBy (\(x,y) (p,q) -> compare q y) (tlist (foldr insertFreq Tip (words x))) 

testF :: (Show t) => ([Char], t) -> [Char] 
testF (x, n) = concat (x : ":" : " \t\t\t " : show n : []) 

concord = putStr . unlines . map testF . sortedTree 

fileFreq filename = do { text <- readFile filename; concord text } 

fileFreq_hs :: CString -> IO CInt 
fileFreq_hs cstr = catch (wrap_fileFreq cstr) (\_ -> return (-1)) 
    where wrap_fileFreq cstr = do 
      str <- peekCString cstr 
      fileFreq str 
      return 0 
foreign export ccall fileFreq_hs :: CString -> IO CInt 
+0

Gracias por la excelente respuesta. – zaplec

+1

Probé tu código y puedo compilarlo sin errores. Sin embargo, hay al menos dos cosas que me pregunto: a) ¿Por qué solo la función llamada fileFreq necesita ser "convertida"? ¿El compilador GHC maneja automáticamente los tipos de subfunción? b) Intenté entender el archivo fuente _stub.c de aspecto muy extraño y, desafortunadamente, no tengo idea de cómo debo ingresar el archivo de texto que me gustaría procesar. La definición de la función fileFreq en el archivo .c es "HsInt32 fileFreq_hs (HsPtr a1)". – zaplec

+1

El único lugar donde debe preocuparse por los tipos es las funciones que desea exportar a C-code. Allí y solo allí necesita convertir entre tipos C y tipos Haskell. Los tipos de subfunción no necesitan ser modificados, y de hecho no deberían ser modificados. El compilador de GHC no hará nada acerca de los tipos de subfunción, sino que convertirás explícitamente a tipos de haskell, luego todas las demás funciones usarán tipos de Haskell. Como usuario C solo necesita incluir el archivo de encabezado generado y luego compilar todos los archivos junto con su archivo C usando GHC (según el enlace que tenía en su pregunta). – dnaq

Cuestiones relacionadas