Skip to content

[eta] Annotations and ADT foreign export #601

@NickSeagull

Description

@NickSeagull

There are many frameworks that depend on annotations. I will be using Spring Boot as an example.

Spring Boot is a widespread Java framework that does not enforce any kind of structure, while being able to use all the Spring Boot modules to make application development easier.

These modules include interoperability with:

  • CassandraDB
  • CouchDB
  • Kafka
  • Many caching systems
  • Queueing systems
  • Mail
  • And many more

The problem here is, Spring Boot is mainly annotation driven. And while we are able to do dependency injection using a custom environment (like a ReaderT), other features of the framework are not accessible because of its hard dependency on annotations.

Of course, one can use the Java Persistence API (JPA) to handle the creation and modification of database entries.

Example application

This is an example of a very simple Spring Boot application that handles persistence and routing:

I'll be using Scala as the example language just for the sake of brevity, as it is less verbose and it is directly translatable to Java.

Application.scala

@SpringBootApplication
class Application { }

def main(args: Array[String]) = {
  SpringApplication.run(classOf[Application], args)
}

This is the entry point of our application. We are passing Application.class to the run method so Spring can operate on it using reflection.

I can imagine doing this easily in Eta:

@SpringBootApplication
data Application = Application @eta.myexample.Application

main :: IO ()
main = do
    args <- getArgs
    runSpringApplication (getClass (Proxy :: Proxy Application)) args

Post.scala

@Entity
class Post {
  @Id
  @GeneratedValue
  var id: Long = null
  var title: String = null
  var content: String = null
}

This class defines an entity that will be stored in the Post table.

I think it would be quite tricky to do so, unless we force the exported ADTs to be records:

@Entity
data Post = Post
    { id :: @Id @GeneratedValue (Mutable Long)
    , title :: Mutable String
    , content :: Mutable String
    }

(Idea: non-records fields could be exported as _1, _2, _n)

PostRepository.scala

trait PostRepository extends JpaRepository[Post, Long] { }

This is just an interface that allows us to use some methods to access our database:

data PostRepository = PostRepository @foo.PostRepository

type instance Inherits PostRepository = '[JpaRepository Post Long]

(Note: I'm not sure if this is the correct way to do so)

PostController.scala

@Controller
@RequestMapping("/")
class PostController {

  @Autowired
  var postRepository: PostRepository = null

  @GetMapping("/")
  def posts(model: Model): String = {
    val posts = postRepository.findAll()
    model.addAttribute("posts", posts)
    "postList"
  }

  @PostMapping("/")
  def addToPostsList(post: Post): String = {
    postRepository.save(post)
    "redirect:/"
  }

}

This is the biggest part, the controller that handles all the requests and uses the other classes

@Controller
@RequestMapping "/"
data PostController = PostController
  { postRepository :: @Autowired (Mutable PostRepository)
  }

@GetMapping "/"
posts :: Model -> Java PostController String
posts model = do
  posts <- postRepository <.> findAll
  model <.> addAttribute "posts" posts
  return "postList"

@PostMapping "/" 
addToPostsList :: Post -> Java PostController String
addToPostsList post = do
  postRepository <.> save post
  return "redirect:/"

It looks quite feasible to me.

Note: I'm not an expert in the Eta export features. Although I'm a Haskeller, I haven't used Eta too much :)

**Note 2: This are just snippets of code, if the whole project is needed, you can find it in this repository, this commit **

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions