¿Cómo hacer un proyecto de Haskell cabal con biblioteca + ejecutables que todavía se ejecutan con runhaskell / ghci?


Si declara una biblioteca + secciones ejecutables en un archivo cabal mientras evita la doble compilación de la biblioteca poniendo la biblioteca en un directorio hs-source-dirs, normalmente ya no puede ejecutar su proyecto con ghci y runhaskell, especialmente si los ejecutables tienen módulos auxiliares.

¿Qué es un diseño de proyecto recomendado que

  • solo construye lo que se necesita una vez
  • permite usar runhaskell
  • tiene una estructura limpia sin hacks?
Author: Community, 2012-09-06

2 answers

Supongamos que tiene una biblioteca mylib, y ejecutables mylib-commandline y mylib-server.

Se usa hs-source-dirs para la biblioteca y cada ejecutable para que cada uno tenga su propia raíz de proyecto, evitando la doble compilación:

mylib/                      # Project root
  mylib.cabal
  src/                      # Root for the library
  tests/
  mylib-commandline/        # Root for the command line utility + helper modules
  mylib-server/             # Root for the web service + helper modules

Disposición del directorio completo:

mylib/                      # Project root
  mylib.cabal
  src/                      # Root for the library
    Web/
      Mylib.hs              # Main library module
      Mylib/
        ModuleA             # Mylib.ModuleA
        ModuleB             # Mylib.ModuleB
  tests/
    ...
  mylib-commandline/        # Root for the command line utility
    Main.hs                 # "module Main where" stub with "main = Web.Mylib.Commandline.Main.main"
    Web/
      Mylib/
        Commandline/
          Main.hs           # CLI entry point
          Arguments.hs      # Programm command line arguments parser
  mylib-server/             # Root for the web service
    Server.hs               # "module Main where" stub with "main = Web.Mylib.Server.Main.main"
    Web/
      Mylib/
        Server/
          Main.hs           # Server entry point
          Arguments.hs      # Server command line arguments parser

El archivo de punto de entrada similar a un trozo mylib-commandline/Main.hs se ve así:

module Main where

import qualified Web.Mylib.Server.Main as MylibServer

main :: IO ()
main = MylibServer.main

Los necesita porque un executable debe comenzar en un módulo simplemente llamado Main.

Su mylib.cabal parece esto:

library
  hs-source-dirs:   src
  exposed-modules:
    Web.Mylib
    Web.Mylib.ModuleA
    Web.Mylib.ModuleB
  build-depends:
      base >= 4 && <= 5
    , [other dependencies of the library]

executable mylib-commandline
  hs-source-dirs:   mylib-commandline
  main-is:          Main.hs
  other-modules:
    Web.Mylib.Commandline.Main
    Web.Mylib.Commandline.Arguments
  build-depends:
      base >= 4 && <= 5
    , mylib
    , [other depencencies for the CLI]

executable mylib-server
  hs-source-dirs:   mylib-server
  main-is:          Server.hs
  other-modules:
    Web.Mylib.Server.Main
  build-depends:
      base >= 4 && <= 5
    , mylib
    , warp >= X.X
    , [other dependencies for the server]

cabal build construirá la biblioteca y los dos ejecutables sin doble compilación de la biblioteca, porque cada uno está en su propio hs-source-dirs y los ejecutables dependen de la biblioteca.

Todavía puede ejecutar los ejecutables con runghc desde la raíz de su proyecto, usando el interruptor -i para indicar dónde buscará los módulos (usando : como separador):

runhaskell -isrc:mylib-commandline mylib-commandline/Main.hs

runhaskell -isrc:mylib-server mylib-server/Server.hs

De esta manera, puede tener un diseño limpio, ejecutables con módulos auxiliares, y todo sigue funcionando con runhaskell/runghc y ghci. Para evitar escribir esta bandera repetidamente, puede agregar algo similar a

:set -isrc:mylib-commandline:mylib-server

A su archivo .ghci.


Tenga en cuenta que a veces debería dividir su código en paquetes separados, p.ej. mylib, mylib-commandline y mylib-server.

 86
Author: nh2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-06 21:07:07

Puede usar cabal repl para iniciar ghci con la configuración del archivo cabal y cabal run para compilar y ejecutar los ejecutables. A diferencia de runhaskell y ghci, usar cabal repl y cabal run también recoge las dependencias de las cajas de arena de cabal correctamente.

 3
Author: Toxaris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-19 08:10:53