【问题标题】:How to write less boilerplate in a expression evaluator written with recursion-schemes如何在使用递归方案编写的表达式评估器中编写更少的样板
【发布时间】:2018-02-03 17:43:30
【问题描述】:

使用recursion-scheme 库 编写抽象语法树和相应的表达式评估器很容易:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveFunctor #-} 
{-# LANGUAGE DeriveFoldable #-} 
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE LambdaCase #-}

import Data.Functor.Foldable 
import Data.Functor.Foldable.TH

data Expr  = Plus Expr  Expr 
           | Mult Expr Expr 
           | Const Expr 
         deriving (Show, Eq)
makeBaseFunctor ''Expr  
-- Write a simple evaluator
eval :: Expr -> Int 
eval = cata alg 
  where 
    alg = \case
      PlusF  x y  -> (+) x y
      MultF  x y  -> (*) x y
      ConstF x    -> x 

现在看看eval的where子句中alg函数的大小写。我认为所有的 xy 变量 应该没有必要。因此,我正在寻找某种方式(语法、语言扩展等) 删除这个样板并写:

  PlusF  -> (+)
  MultF  -> (*)
  ConstF -> id 

【问题讨论】:

    标签: haskell syntax boilerplate recursion-schemes


    【解决方案1】:

    https://hackage.haskell.org/package/catamorphism-0.5.1.0/docs/Data-Morphism-Cata.html 推导出ExprF 的变态。

    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE DeriveFunctor #-} 
    {-# LANGUAGE DeriveFoldable #-} 
    {-# LANGUAGE DeriveTraversable #-}
    {-# LANGUAGE TemplateHaskell #-}
    
    import Data.Functor.Foldable 
    import Data.Functor.Foldable.TH
    import Data.Morphism.Cata
    
    data Expr 
      = Plus Expr Expr 
      | Mult Expr Expr 
      | Const Expr 
      deriving (Show, Eq)
    makeBaseFunctor ''Expr
    $(makeCata defaultOptions ''ExprF)
    
    -- Write a simple evaluator
    eval :: Expr -> Int 
    eval = cata $ exprF (+) (*) id
    

    请注意,它还可以推导出 Expr 的变态,产生 eval = expr (+) (*) id 并让您跳过此特定用例的 Data.Functor.Foldable.TH

    【讨论】:

    • 我个人不喜欢exprF (+) (*) id 调用函数的方式。看起来容易出错,因为它很容易弄乱参数(交换+*)。明确指定从构造函数到函数的映射对我来说看起来更好。
    【解决方案2】:

    或者,您可以重构您的语言,使其一方面具有二元运算,另一方面具有一元运算。你会写:

    data BinOp = PlusOp | MultOp deriving (Show, Eq)
    data UnOp  = ConstOp deriving (Show, Eq)
    
    data Expr  = Bin BinOp Expr  Expr
               | Un  UnOp  Expr
             deriving (Show, Eq)
    makeBaseFunctor ''Expr
    

    然后评估者变成:

    eval :: Expr -> Int
    eval = cata $ \case
      BinF op l r -> bin op l r
      UnF  op v   -> un op v
      where
        bin = \case
          PlusOp -> (+)
          MultOp -> (*)
    
        un = \case
          ConstOp -> id
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-31
      • 2017-01-25
      • 2014-06-22
      • 2018-10-18
      • 1970-01-01
      • 1970-01-01
      • 2022-11-03
      • 2023-03-13
      相关资源
      最近更新 更多