2017-12-06 28 views
10

tôi cần phải có một cái gì đó giống nhưHaskell, GHC 8: tự động tải/mô-đun nhập khẩu

-- Main.hs 
module Main where 

main :: IO() 
main = do 
    <import Plugin> 
    print Plugin.computation 

Với Plugin như

-- Plugin.hs 
module Plugin where 

computation :: Int 
computation = 4 

Tuy nhiên, tôi cần các plugin được biên dịch cùng với các ứng dụng chính . Chúng cần được triển khai cùng nhau. Chỉ việc nhập (không phải biên dịch) của mô-đun sẽ diễn ra tự động.

Tôi tìm thấy Dynamically loading compiled Haskell module - GHC 7.6 trên đường đi và nó hoạt động tốt với GHC 8.0.2 ngoại trừ việc nó yêu cầu tệp nguồn của plugin nằm trong thư mục làm việc hiện tại khi thực thi ứng dụng.


Sửa (07.12.2017)

Có thể tải một mô-đun từ một String thay vì một tập tin bằng cách sử dụng API GHC? http://hackage.haskell.org/package/ghc-8.2.1/docs/GHC.html#t:Target cho thấy rằng nó có thể, nhưng tài liệu có nhiều lỗ và tôi không thể tìm thấy một cách để thực sự làm điều này. Nếu điều này có thể được thực hiện, tôi có thể sử dụng file-embed để bao gồm tệp nguồn plugin vào nhị phân đã biên dịch. Ví dụ:

module Main where 

-- Dynamic loading of modules 
import GHC 
import GHC.Paths (libdir) 
import DynFlags 
import Unsafe.Coerce 

import Data.Time.Clock (getCurrentTime) 
import StringBuffer 

pluginModuleNameStr :: String 
pluginModuleNameStr = "MyPlugin" 

pluginSourceStr :: String 
pluginSourceStr = unlines 
    [ "module MyPlugin where" 
    , "computation :: Int" 
    , "computation = 4" 
    ] 

pluginModuleName :: ModuleName 
pluginModuleName = mkModuleName pluginModuleNameStr 

pluginSource :: StringBuffer 
pluginSource = stringToStringBuffer pluginSourceStr 

main :: IO() 
main = do 
    currentTime <- getCurrentTime 
    defaultErrorHandler defaultFatalMessager defaultFlushOut $ do 
     result <- runGhc (Just libdir) $ do 
     dflags <- getSessionDynFlags 
     setSessionDynFlags dflags 
     let target = Target { targetId = TargetModule $ pluginModuleName 
          , targetAllowObjCode = True 
          , targetContents = Just (pluginSource 
                , currentTime 
                ) 
          } 
     setTargets [target] 
     r <- load LoadAllTargets 
     case r of 
      Failed -> error "Compilation failed" 
      Succeeded -> do 
      setContext [IIDecl $ simpleImportDecl pluginModuleName] 
      result <- compileExpr ("MyPlugin.computation") 
      let result' = unsafeCoerce result :: Int 
      return result' 
     print result 

này, tuy nhiên, kết quả trong

<command-line>: panic! (the 'impossible' happened) 
    (GHC version 8.0.2 for x86_64-apple-darwin): 
    module ‘MyPlugin’ is a package module 

Sửa (08.12.2017)

tôi có thể biên dịch "cắm" trực tiếp vào nhị phân thức bằng cách viết nguồn vào một tập tin tạm thời và sau đó tải nó như trong bài liên kết (Dynamically loading compiled Haskell module - GHC 7.6). Tuy nhiên, điều này không chơi tốt nếu nhập khẩu Plugin gói từ Hackage:

module Main where 

import Control.Monad.IO.Class (liftIO) 
import DynFlags 
import GHC 
import GHC.Paths (libdir) 
import System.Directory (getTemporaryDirectory, removePathForcibly) 
import Unsafe.Coerce (unsafeCoerce) 

pluginModuleNameStr :: String 
pluginModuleNameStr = "MyPlugin" 

pluginSourceStr :: String 
pluginSourceStr = unlines 
    [ "module MyPlugin where" 
    , "import Data.Aeson" 
    , "computation :: Int" 
    , "computation = 4" 
    ] 

writeTempFile :: IO FilePath 
writeTempFile = do 
    dir <- getTemporaryDirectory 
    let file = dir ++ "/" ++ pluginModuleNameStr ++ ".hs" 
    writeFile file pluginSourceStr 
    return file 

main :: IO() 
main = do 
    moduleFile <- writeTempFile 
    defaultErrorHandler defaultFatalMessager defaultFlushOut $ do 
    result <- runGhc (Just libdir) $ do 
     dflags <- getSessionDynFlags 
     setSessionDynFlags dflags 
     target <- guessTarget moduleFile Nothing 
     setTargets [target] 
     r <- load LoadAllTargets 
     liftIO $ removePathForcibly moduleFile 
     case r of 
     Failed -> error "Compilation failed" 
     Succeeded -> do 
      setContext [IIDecl $ simpleImportDecl $ mkModuleName pluginModuleNameStr] 
      result <- compileExpr "MyPlugin.computation" 
      let result' = unsafeCoerce result :: Int 
      return result' 
    print result 

Có cách nào để tải các gói khi, ví dụ, MyPlugin chứa các tuyên bố import Data.Aeson? Nếu tôi thêm nó vào chuỗi plugin, nó không thành công với

/var/folders/t2/hp9y8x6s6rs7zg21hdzvhbf40000gn/T/MyPlugin.hs:2:1: error: 
    Failed to load interface for ‘Data.Aeson’ 
    Perhaps you meant Data.Version (from base-4.9.1.0) 
    Use -v to see a list of the files searched for. 
haskell-loader-exe: panic! (the 'impossible' happened) 
    (GHC version 8.0.2 for x86_64-apple-darwin): 
    Compilation failed 
CallStack (from HasCallStack): 
    error, called at app/Main.hs:40:19 in main:Main 

Lý do yêu cầu của tôi là hỗ trợ cơ sở dữ liệu: Chúng tôi sử dụng liên tục để truy cập cơ sở dữ liệu và nhập khẩu năng động là cần thiết để hỗ trợ nhiều cơ sở dữ liệu (MySQL , PostgreSQL và SQLite) trong khi vẫn cho phép người dùng cuối chỉ cài đặt một trong ba máy chủ cơ sở dữ liệu (với các từ khác: không yêu cầu người dùng cài đặt tất cả chúng nếu họ chỉ sử dụng, ví dụ, PostgreSQL). Mô-đun cụ thể cho cơ sở dữ liệu chỉ nên được tải khi người dùng thực sự định cấu hình ứng dụng chính để sử dụng mô-đun đó.

Nếu tôi không import Database.Persist.MySQL, thì ứng dụng sẽ không yêu cầu cài đặt MySQL. Nếu không, ứng dụng sẽ không thành công, ví dụ:

dyld: Library not loaded: 
/usr/local/opt/mysql/lib/libmysqlclient.20.dylib 

trên macOS.

+0

Tôi có cảm giác rằng đây có thể là trường hợp sử dụng cho ghc [backpack] (https://plv.mpi-sws.org/backpack/), nhưng không thể chắc chắn vì tôi chưa sử dụng nhưng bản thân tôi. Ngoài ra nó chỉ có sẵn trên ghc-8.2 –

+0

Cảm ơn, nhưng tôi không chắc liệu chúng tôi có thể nâng cấp dự án lên GHC 8.2 vào thời điểm này hay không. Ngoài ra, chúng tôi sử dụng Stack, không tương thích với ba lô tại thời điểm này (mặc dù họ đang làm việc trên nó). – eugenk

+0

Tôi đã chỉnh sửa bài đăng để hiển thị cách tiếp cận hiện tại của mình. – eugenk

Trả lời

1

Một tệp có tên mô-đun phù hợp phải tồn tại theo giao diện của nó - có vẻ như nội dung của tệp không quan trọng.

Trên Linux, tôi thậm chí có thể biến nó thành liên kết tượng trưng thành/dev/null và mọi thứ hoạt động nhưng không có liên kết tượng trưng.

+0

Nếu có ai biết cách buộc mô-đun được diễn giải thì có lẽ không có tệp nào trên đĩa được yêu cầu. Nếu có, vui lòng thêm kiến ​​thức của bạn! – codeshot

+0

Ngay cả khi bắt buộc mô-đun được diễn giải một tệp trên đĩa là bắt buộc. Tôi đã tìm cách để móc API của GHC nơi nó truy cập vào hệ thống tập tin nhưng có vẻ như GHC sử dụng đơn nguyên IO cho điều đó và không có loại lớp mô-đun kiểu ModuleStore mà người ta có thể thực hiện. – codeshot

Các vấn đề liên quan