Snippets

Brian McKenna Abstracting over Java and Python classes

Created by Brian McKenna last modified
{-# LANGUAGE OverloadedStrings #-}

module AbstractClasses where

import Control.Monad (void)
import Control.Monad.Trans.Writer (Writer(), execWriter, runWriter, tell)
import Data.String (IsString(..))
import qualified Language.Java.Pretty as Java
import qualified Language.Java.Syntax as Java
import qualified Language.Python.Common.AST as Py
import qualified Language.Python.Common.Pretty as Py
import Language.Python.Common.PrettyAST ()

example ::
  ( Monad classWriter
  , Monad methodWriter
  , IsString className
  , IsString methodName
  , IsString typeVariableName
  , IsString paramName
  ) =>
  (className -> classWriter () -> class_) ->
  (methodName -> methodWriter (methodBody, typeVariable) -> classWriter staticMethod) ->
  (typeVariableName -> methodWriter typeVariable) ->
  (paramName -> typeVariable -> methodWriter param) ->
  (param -> methodBody) ->
  class_
example class_ staticMethod typeParam param var =
  class_ "Example" $
    void . staticMethod "id" $ do
      a <- typeParam "A"
      x <- param "x" a
      return (var x, a)

-- How to build a Java class.

javaClass :: String -> Writer [Java.Decl] () -> Java.ClassDecl
javaClass n =
  Java.ClassDecl [Java.Public] (Java.Ident n) [] Nothing [] . Java.ClassBody . execWriter

javaStaticMethod ::
  String
  -> Writer ([Java.TypeParam], [Java.FormalParam]) (Java.Exp, Java.Type)
  -> Writer [Java.Decl] ()
javaStaticMethod n b =
  let ((e, rt), (tps, fps)) = runWriter b
      ret = Java.MethodBody . Just . Java.Block . (:[]) . Java.BlockStmt . Java.Return $ Just e
  in tell [Java.MemberDecl $ Java.MethodDecl [Java.Public, Java.Static] tps (Just rt) (Java.Ident n) fps [] ret]

javaTypeVariable :: String -> Writer ([Java.TypeParam], [Java.FormalParam]) Java.Type
javaTypeVariable n = do
  tell ([Java.TypeParam (Java.Ident n) []], mempty)
  return . Java.RefType . Java.ClassRefType $ Java.ClassType [(Java.Ident n, [])]

javaParameter :: String -> Java.Type -> Writer ([Java.TypeParam], [Java.FormalParam]) Java.Name
javaParameter n t = do
  tell (mempty, [Java.FormalParam [] t False . Java.VarId $ Java.Ident n])
  return . Java.Name . (:[]) $ Java.Ident n

javaVar :: Java.Name -> Java.Exp
javaVar =
  Java.ExpName

-- How to build a Python class.

pythonClass :: String -> Writer [Py.Statement ()] () -> Py.Statement ()
pythonClass n b =
  Py.Class (Py.Ident n ()) [] (execWriter b) ()

pythonStaticMethod :: String -> Writer [Py.Parameter ()] (Py.Expr (), ()) -> Writer [Py.Statement ()] ()
pythonStaticMethod n b =
  let ((e, ()), fps) = runWriter b
      staticmethod = Py.Decorator [Py.Ident "staticmethod" ()] [] ()
  in tell [Py.Decorated [staticmethod] (Py.Fun (Py.Ident n ()) fps Nothing [Py.Return (Just e) ()] ()) ()]

pythonParameter :: String -> () -> Writer [Py.Parameter ()] (Py.Ident ())
pythonParameter n () = do
  tell [Py.Param (Py.Ident n ()) Nothing Nothing ()]
  return $ Py.Ident n ()

pythonVar :: Py.Ident () -> Py.Expr ()
pythonVar =
  flip Py.Var ()

untypedTypeVariable :: Applicative f => b -> f ()
untypedTypeVariable =
  const $ pure ()

-- Instantiation

javaExample :: Java.ClassDecl
javaExample =
  example javaClass
          javaStaticMethod
          javaTypeVariable
          javaParameter
          javaVar

pythonExample :: Py.Statement ()
pythonExample =
  example pythonClass
          pythonStaticMethod
          (untypedTypeVariable :: String -> Writer [Py.Parameter ()] ())
          pythonParameter
          pythonVar

main :: IO ()
main = do
  putStrLn $ Java.prettyPrint javaExample
  -- public class Example
  -- {
  --   public static <A> A id (A x)
  --   {
  --     return x;
  --   }
  -- }

  putStrLn $ Py.prettyText pythonExample
  -- class Example:
  --     @staticmethod
  --     def id(x):
  --         return x

Comments (0)