如何在Haskell中创建乳胶providecommand的类似物?

如何在Haskell中创建乳胶providecommand的类似物?,haskell,compiler-errors,ghc,Haskell,Compiler Errors,Ghc,假设我们正在导入一个模块M,其中需要一个函数f1。但它可能不在那里。但是,我们不希望出现编译错误,但希望执行以下操作(伪代码): 换句话说,我想在Haskell中模拟Latex\providecommand: 这可能是个坏主意。然而,如果你真的想这么做,@WillemVanOnsem的建议会奏效 您可以创建以下单独的模块来定义模板Haskell(TH)函数providedCommand。请注意,由于TH“阶段限制”的限制,您必须在一个单独的模块中执行此操作,而不是在要使用providedComm

假设我们正在导入一个模块
M
,其中需要一个函数
f1
。但它可能不在那里。但是,我们不希望出现编译错误,但希望执行以下操作(伪代码):

换句话说,我想在Haskell中模拟Latex
\providecommand


这可能是个坏主意。然而,如果你真的想这么做,@WillemVanOnsem的建议会奏效

您可以创建以下单独的模块来定义模板Haskell(TH)函数
providedCommand
。请注意,由于TH“阶段限制”的限制,您必须在一个单独的模块中执行此操作,而不是在要使用
providedCommand
的模块中执行此操作,因为TH功能不能在定义它们的同一模块中使用(从技术上讲,不能“拼接”)

module ProvideCommand where

import Language.Haskell.TH

provideCommand :: String -> Q [Dec] -> Q [Dec]
provideCommand nam defn = do
  mval <- lookupValueName nam
  case mval of
    Just _ -> return []
    Nothing -> defn
如果名称
double
尚未定义(例如,在模块
M
中),这将通过在准文本
[d |…|]
中输出声明来定义它。您可以包含多个声明,包括除
double
以外的内容,但显然,除非触发
providedCommand
,否则这些声明将不可用:

provideCommand "double"
  [d| double :: Int -> Int
      double 0 = 0
      double n = addTwo (double (n-1))

      addTwo :: Int -> Int
      addTwo x = x + 2
  |]

你可以使用模板Haskell来解决这个问题:你为什么想要这个?也许有比这更好的方法来实现你的最高目标。例如,如果要支持API已更改的库的多个版本,请使用
CPP
,cabal将为您定义适当的版本检查宏。@DanielWagner,仅供参考,这些版本检查宏过去由cabal定义,但在您的敦促下,现在由GHC定义。因为无论构建系统如何,您都需要它们,而且编译器无论如何都需要这些信息来编译模块,所以公开它基本上是免费的。
{-# LANGUAGE TemplateHaskell #-}

import M
import ProvideCommand

provideCommand "double" [d| double x = x * 2 |]

main = print (double 15)
provideCommand "double"
  [d| double :: Int -> Int
      double 0 = 0
      double n = addTwo (double (n-1))

      addTwo :: Int -> Int
      addTwo x = x + 2
  |]