Module collisions in multi-dar projects

This is a follow-on from https://discuss.daml.com/t/behaviour-of-relative-paths-in-source-in-multi-dar-projects. Repeating the example for context here:

I have the following directory structure setup, to support building of multiple *.dar files (an interface and an implementation):

src/interface/daml.yaml
src/implementation/daml.yaml
src/main/daml.yaml
src/main/daml/Daml/Finance/Implementation/Token.daml
src/main/daml/Daml/Finance/Interface/Transferable.daml
src/main/daml/Daml/Finance/Interface/Issuable.daml
src/main/daml/Daml/Finance/Types.daml
src/main/daml/Daml/Finance/Test/Token.daml

All the source code resides in src/main. the other two directories just contain daml.yaml files that reference subdirectories, i.e.:

cat src/interface/daml.yaml 

source: ../main/daml/Daml/Finance/Interface



cat src/implementation/daml.yaml

source: ../main/daml/Daml/Finance/Implementation

dependencies:
  - ../interface/.daml/dist/daml-finance-interface-0.0.1.dar

Now I’ve noticed some rather strange behaviour. There is a common file, src/main/daml/Daml/Finance/Types.daml that has some common type definitions. It sits outside of the Interface or Implementation directories. Yet it is pulled into the builds (I can see this with damlc inspect).

What happens if I have dependencies to these common types in both the interface and implementation package? Shouldn’t there be some sort of a package clash, because it’s being included in both of them now? Is there any way to prevent this kind of behaviour? For instance, could I use the build-options : -I directive instead of source : ? Is there any difference here?

If a third project imports both of the dars you generate

dependencies
  - daml-finance-interface-0.0.1.dar
  - daml-finance-implementation-0.0.1.dar

and then from a new module you try to import one of the modules defined in both of these, you will get an error

  Ambiguous module name ‘Daml.Finance.Types’:
  it was found in multiple packages:
  daml-finance-interface-1.0.0 daml-finance-implementation-1.0.0

You can get around that one with package-qualified imports,

import qualified "daml-finance-interface" Daml.Finance.Types as Interface.Types
import qualified "daml-finance-implementation" Daml.Finance.Types as Implementation.Types

but then the types themselves wouldn’t match

foos = [Interface.Types.Foo, Implementation.Types.Foo]
  • Couldn't match expected type ‘Implementation.Types.Foo’
  with actual type ‘Interface.Types.Foo’
  NB: ‘Interface.Types.Foo’
  is defined in ‘Daml.Finance.Types’ in package ‘daml-finance-interface-1.0.0’
  ‘Implementation.Types.Foo’
  is defined in ‘Daml.Finance.Types’ in package ‘daml-finance-implementation-1.0.0’
  • In the expression: Interface.Types.Foo
  In the expression: [Implementation.Types.Foo, Interface.Types.Foo]
  In an equation for ‘foos’: foos = [Implementation.Types.Foo, Interface.Types.Foo]

So, my suggestion would be to split off the shared parts into a separate package that can be depended on by both interface and implementation packages. I think in that case these two wouldn’t need the .. parent directory in their configuration files, since they would just depend on the types package directly. The main drawback would be needing to build the dars sequentially, but that can be taken care of outside of damlc.

1 Like