# \[ANN\] PrivateFields.jl

**URL:** <https://discourse.julialang.org/t/ann-privatefields-jl/98454>\
**Category:** Package Announcements\
**Created:** [May 8, 2023, 1:43am UTC](https://discourse.julialang.org/t/ann-privatefields-jl/98454 "2023-05-08T01:43:37Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![sbuercklin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sbuercklin/32/15728_2.png) [@sbuercklin](https://discourse.julialang.org/u/sbuercklin)\
**Post date:** [May 8, 2023, 1:43am UTC](https://discourse.julialang.org/t/ann-privatefields-jl/98454/1 "2023-05-08T01:43:37Z")

</div>

GitHub link: [GitHub - SBuercklin/PrivateFields.jl: No touching!](https://github.com/SBuercklin/PrivateFields.jl) (should land on the General Registry in ~3 days from posting)

# Overview

`PrivateFields.jl` implements the `@private_struct, @private_method` macros.

`@private_struct` lets you tag fields of `struct`s as `@private`, in which case those fields cannot be accessed through the traditional dot-syntax `a.b`.

The exception to this behavior is in methods tagged with `@private_method` and a special syntax to specify which arguments should be allowed to have privileged access to the private fields within that method. A simple demo can be found in the Example section.

# Motivation

A common pattern in Julia packages is to build a core package which defines interfaces in library packages, typically with some core types supporting that interface. Users then interact with these types, ideally through the defined interface. However, Julia also allows users to interact directly with the fields of these types through the dot syntax. This usage can lead to users relying on implementation details of types, such as particular fieldnames, rather than the function interfaces to these types.

`PrivateFields.jl` aims to make interacting with the types through the convenient dot-syntax less ergonomic, encouraging the use of a preferred API rather than implicit interfaces through fieldnames (e.g. getter/setter methods).

At the same time, the dot-syntax does serve a purpose: developers of the original type could use the dot syntax for implementing, maintaining, and testing these interfaces, as they are responsible for the internal layout of the `struct` (fieldnames, order of fields, etc). The `@private_method` macro lets developers implement code which temporarily permits the dot syntax within the scope of that method for select arguments.

My goal is that by encouraging the use of function interfaces, it becomes easier to shrink the API that needs to be supported for inter-project compatibility. Developers should feel less constrained by how their types are defined, and users should feel more comfortable that new releases continue to be backwards compatible through the interfaces they use.

# Example

```julia
# @private denotes the private fields
@private_struct struct Foo{X,Y}
    @private x::X
    y::Y
end

foo1 = Foo(3.0, 4.0)
foo2 = Foo(5, 6)

# 4-colon syntax denotes that only f1 should allow private access to fields
@private_method f(f1::::Foo, f2::Foo) = f1.x + f2.y
g(f1::Foo, f2::Foo) = f1.x + f2.y

f(foo1, foo2) # 9.0

g(foo1, foo2) # ERROR: PrivacyError: Attempted to directly access private field Foo.x outside private context

```

# Remaining Work

## Fully General `struct` Support

Right now only immutable types are supported, mostly as a proof of concept. Mutable types should be implemented as well, which will need their own implementation on the `setproperty!` side.

## Settle on the “correct” approach

This approach works well if you don’t want a custom `getproperty` implementation. If you do want a custom `getproperty` on top of private fields, this package requires you to implement `PrivateFields.getproperty_direct` instead.

I had a couple other ideas for how we toggle the `@private_method` mode, but I’m open to input on a better way to structure a solution to this.

## Modes of Operation

At startup, it might be helpful to set an environment variable which enables/disables this behavior, possibly on a per-project basis.

## Restrict where `@private_method` can be used

Right now, there’s nothing stopping downstream users from tagging their code with `@private_method` and continuing to use the dot syntax. One thought I had was to only permit `@private_method` in the same module where the corresponding types are defined. This enforces that users cannot directly access fields with the dot syntax, while still allowing developers to access the internals.

# Extra Thoughts

## Why Not Just Use `getproperty`?

Maintaining `getproperty` implementations is cumbersome, especially if you’re changing the fields you want to store in the underlying `struct`. I find it’s easier to maintain the individual methods that perform a calculation to retrieve a value than a list of `if-elseif` statements where I check the symbol to implement how a value should be computed.

## On Specifying Interfaces

The problem which accompanies this is actually defining interfaces. I like `BinaryTraits.jl` for not only defining, but also **testing** my interfaces in library packages. The testing performed by `BinaryTraits.jl` isn’t over correctness, but merely whether the proper methods have been implemented for a type to satisfy all attached interfaces.

I developed this package with `BinaryTraits.jl` in mind for enforcing the `getter` methods (and later, `setter!` methods) that define the interface. In other words, `BinaryTraits.jl` tells you what you need to implement, and `PrivateFields.jl` keeps you honest about obeying those interfaces.
