# Is there a way to ‘hide’ temporarily-unwanted, and potentially dangerous, values in a record?

**URL:** <https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517>\
**Category:** General\
**Tags:** beginners\
**Created:** [January 19, 2024, 2:33pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517 "2024-01-19T14:33:17Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [January 19, 2024, 2:33pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/1 "2024-01-19T14:33:17Z")

</div>

This could be a long-shot but…

I have some code, below, which I have stripped down from the real code. (Apologies for the length but at least it can be evaluated in the FSI.)

I am reading some records from a database, converting them into Domain records, doing some modifications, and then saving them back to the database, but that’s probably not important.

When a `MyThing` value is of type `SingleDigit`, only the `AllowZero` value is valid.  
When a `MyThing` value is of type `MultiDigit`, only the `NumberOfDigits` and `Suffix` values are valid.

In the code this works as expected but, I would like to be able to keep the `AllowZero` value and the `Suffix` value ‘behind the scenes’ when switching between the two in `changeNumberOfDigits`, so the previous values can be restored when the type changes rather than hard-coding some defaults, which isn’t currently possible with this design.

Basically, when the user reduces the number of digits to one I want to somehow keep the previous value of `Suffix` so it can be reinstated if the number of digits goes higher than one again. Same sort of thing for `AllowZero` but the other way round.

I did another version where I kept these fields in each type of MyThing, e.g.:

```auto
type SingleDigit = { AllowZero : bool ; Suffix : string option } 
type MultiDigit = { NumberOfDigits : int ; Suffix : string option ; AllowZero : bool }

```

…and just ignored them when they weren’t needed, but I was wondering if there was a better way.

I thought about storing the ‘missing’ values in other fields with different names (e.g. AllowZeroBackup in MultiDigit) but that’s not much better and could be even more confusing.

Every change will result in a database overwrite of the original record so using that as a ‘backup’ isn’t an option.

Holding the ‘missing’ values in another separate record would probably not be a good idea either, and it would certainly make things more complicated.

This code is only being, and will only be, used by me personally so as long as I document things nicely there probably won’t be a problem with the field duplication version but I would like to know if there is a better way in case I need it later.

And here’s the code:

```auto
open System 

type SingleDigit = { AllowZero : bool } 

type MultiDigit = { NumberOfDigits : int ; Suffix : string option }

type MyThing = 
    | Single of SingleDigit 
    | Multi of MultiDigit 

let changeNumberOfDigits number pattern = 
    match pattern, number with 
    | Single _, 1 -> pattern 
    | Single _, _ -> Multi { NumberOfDigits = number ; Suffix = Some "hello" } 
    | Multi _, 1 -> Single { AllowZero = false } 
    | Multi existing, _ -> Multi { existing with NumberOfDigits = number }

let toggleAllowZero pattern = 
    match pattern with 
    | Single { AllowZero = allowZero } -> Single { AllowZero = not <| allowZero } 
    | Multi m -> Multi m 

let changeSuffix newSuffix pattern = 
    match pattern with 
    | Single s -> Single s 
    | Multi m -> Multi { m with Suffix = newSuffix } 

let printValue = 
    let ofCharArray (chars : char array) = System.String(chars) 
    fun pattern -> 
        do 
            match pattern with 
            | Single {AllowZero = allowZero} when allowZero = true -> 
                printfn "0"
            | Single _ -> 
                printfn "5" 
            | Multi { NumberOfDigits = numberOfDigits ; Suffix = suffix } -> 
                let number = 
                    '9' 
                    |> Array.replicate numberOfDigits 
                    |> ofCharArray 
                suffix 
                |> Option.defaultValue String.Empty   
                |> (+) number  
                |> printfn "%s" 
        pattern 

Single { AllowZero = true } 
|> printValue 
|> changeNumberOfDigits 4 // AllowZero value is lost here.
|> printValue 
|> changeSuffix (Some "changed") 
|> printValue 
|> changeNumberOfDigits 1 // Suffix is lost here.
|> printValue 
|> toggleAllowZero 
|> printValue 
|> changeNumberOfDigits 6 // AllowZero value is lost here again.
|> printValue 
|> changeNumberOfDigits 1 // Suffix is lost here again.
|> printValue

```

---

<div class="post-metadata">

**Author:** ![ntwilson](https://yyz2.discourse-cdn.com/flex030/user_avatar/forums.fsharp.org/ntwilson/32/984_2.png) [@ntwilson](https://forums.fsharp.org/u/ntwilson)\
**Post date:** [January 19, 2024, 4:00pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/2 "2024-01-19T16:00:35Z")

</div>

You could play around with private record constructors. Something like

```auto
module Digits =
  // can only be constructed or have fields access within the same module
  type SingleDigit = private { AllowZero : bool; Suffix : string option } with 
    // new member to allow public access to the AllowZero field
    member this.AllowZero = this.AllowZero 

  type MultiDigit = private { NumberOfDigits : int; Suffix : string option; AllowZero : bool } with
    member this.NumberOfDigits = this.NumberOfDigits
    member this.Suffix = this.Suffix

  let initializeNewSingleDigit allowZero = 
    { AllowZero = allowZero; Suffix = None }

  let initializeNewMultiDigit numDigits suffix = 
    { NumberOfDigits = numDigits; Suffix = suffix; AllowZero = false }

  type MyThing = 
    | Single of SingleDigit 
    | Multi of MultiDigit   
  
  let changeNumberOfDigits number pattern = 
    match pattern, number with 
    | Single _, 1 -> pattern 
    | Single s, _ -> Multi { NumberOfDigits = number ; Suffix = s.Suffix } 
    | Multi m, 1 -> Single { AllowZero = m.AllowZero } 
    | Multi existing, _ -> Multi { existing with NumberOfDigits = number }

  ...

```

WARNING: I didn’t run that through a compiler, so it might not work. Just a starting point.

From outside the `Digits` module, you don’t have access to do `.Suffix` on a SingleDigit, or to do `.AllowZero` on a MultiDigit, AND you also don’t have access to construct values or to do copy & update. If you need more fine-grained control than that, you’re probably going to need to abandon records and use a class.

---

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [January 21, 2024, 12:55pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/3 "2024-01-21T12:55:57Z")

</div>

That’s an interesting possible solution.

I’m having a bit of trouble getting a version that will compile but I’ll carry on and see if I can get somewhere with it.

Cheers.

---

<div class="post-metadata">

**Author:** ![ntwilson](https://yyz2.discourse-cdn.com/flex030/user_avatar/forums.fsharp.org/ntwilson/32/984_2.png) [@ntwilson](https://forums.fsharp.org/u/ntwilson)\
**Post date:** [January 22, 2024, 2:16am UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/4 "2024-01-22T02:16:53Z")

</div>

Yeah, made a couple mistakes in there. I think the big thing is you can’t have a member with the same name as one of the record fields. I just made all the (private) record fields lowercase which breaks convention, but works.  
Here’s a complete version:

```auto
open System

module Digits =
  // can only be constructed or have fields access within the same module
  type SingleDigit = private { allowZero : bool; suffix : string option } with 
    // new member to allow public access to the AllowZero field
    member this.AllowZero = this.allowZero 

  type MultiDigit = private { numberOfDigits : int; suffix : string option; allowZero : bool } with
    member this.NumberOfDigits = this.numberOfDigits
    member this.Suffix = this.suffix

  let initializeNewSingleDigit allowZero = 
    { allowZero = allowZero; suffix = None }

  let initializeNewMultiDigit numDigits suffix = 
    { numberOfDigits = numDigits; suffix = suffix; allowZero = false }

  type MyThing = 
    | Single of SingleDigit 
    | Multi of MultiDigit   
  
  let changeNumberOfDigits number pattern = 
    match pattern, number with 
    | Single _, 1 -> pattern 
    | Single s, _ -> Multi { numberOfDigits = number ; suffix = s.suffix; allowZero = s.AllowZero } 
    | Multi m, 1 -> Single { allowZero = m.allowZero; suffix = m.Suffix } 
    | Multi existing, _ -> Multi { existing with numberOfDigits = number }

  let toggleAllowZero pattern = 
    match pattern with 
    | Single ({ allowZero = allowZero } as s) -> Single { s with allowZero = not <| allowZero } 
    | Multi m -> Multi m 

  let changeSuffix newSuffix pattern = 
    match pattern with 
    | Single s -> Single s 
    | Multi m -> Multi { m with suffix = newSuffix } 

open Digits
// from here down, you can't access the `suffix` field of a `SingleDigit`, 
// or the `allowZero` field of a `MultiDigit`

let printValue = 
  let ofCharArray (chars : char array) = System.String(chars) 
  fun pattern -> 
    do 
      match pattern with 
      | Single s when s.AllowZero = true -> 
        printfn "0"
      | Single _ -> 
        printfn "5" 
      | Multi m -> 
        let number = 
          '9' 
          |> Array.replicate m.NumberOfDigits 
          |> ofCharArray 
        m.Suffix 
        |> Option.defaultValue String.Empty   
        |> (+) number  
        |> printfn "%s" 
    pattern 

Single (initializeNewSingleDigit true)
|> printValue 
|> changeNumberOfDigits 4
|> printValue 
|> changeSuffix (Some "changed") 
|> printValue 
|> changeNumberOfDigits 1
|> printValue 
|> toggleAllowZero 
|> printValue 
|> changeNumberOfDigits 6
|> printValue 
|> changeNumberOfDigits 1
|> printValue
|> ignore

```

---

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [January 24, 2024, 2:07pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/5 "2024-01-24T14:07:16Z")

</div>

Apologies for the delay in my reply.

Thanks for sticking with this; your new version looks interesting.

There are a couple of things that I’m not sure about, e.g.  
`type TypeName = private {...}` (never seen the `private` in that position before)  
…and…  
`| Single ({ allowZero = allowZero } as s)` (never seen that combination before)  
…so I’ll need to look at the documentation to see how they work.

Once I’m a bit clearer on what’s happening I try the same technique the real code and see what happens.

---

<div class="post-metadata">

**Author:** ![ntwilson](https://yyz2.discourse-cdn.com/flex030/user_avatar/forums.fsharp.org/ntwilson/32/984_2.png) [@ntwilson](https://forums.fsharp.org/u/ntwilson)\
**Post date:** [January 24, 2024, 3:04pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/6 "2024-01-24T15:04:35Z")

</div>

The `type TypeName = private { ... }` is the main F# records feature I’m trying to showcase with the example I posted. That means that you can only access most of the built-in record functionality from within the same module or namespace that the record was defined in. External to the module or namespace, you can’t construct it with normal record syntax, you can’t access any of the fields (this is the desired trait for your use), and you can’t copy-and-update (`{ x with ... }`). You can though write members for all the public functionality you want to expose, or functions within the same module/namespace. If you want more fine-grained control over what’s public and private than that, you’ll have to abandon records and move towards classes.

As for `| Single ({ allowZero = allowZero } as s)`:  
In your original code, you had the line

```auto
| Single { AllowZero = allowZero } -> Single { AllowZero = not <| allowZero } 

```

So I’ll assume you know how destructuring a record works. I had to change the field name from `AllowZero` to `allowZero` because I wanted a public member `AllowZero`. The `as s` there means that while I want to destructure the record to extract the `allowZero` value, I _also_ want to be able to refer to the whole record as `s`. I could have just skipped destructuring and done

```auto
| Single s -> Single { s with allowZero = not <| s.allowZero }

```

---

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [January 29, 2024, 9:44am UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/7 "2024-01-29T09:44:51Z")

</div>

Again, apologies for another delay in my reply, and thanks for sticking with this.

The concern I had with `private` was where it was in that line of code.

The official documentation I was looking at [Records in F# | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/records) shows the accessibility modifier before the type name, not after the equals sign, and so I was confused as to whether your example was a special case which I couldn’t find in the documentation.

As for the other code, I’d just never seen de-structuring and binding (if that’s the correct term here) done at the same time.

It just ‘threw me’ a bit is all but it makes sense once I understand what’s going on.

Anyway, the code you gave looks like it works just fine so I’ll be taking my cue from that when I get back to working on the real code.

Cheers.

---

<div class="post-metadata">

**Author:** ![ntwilson](https://yyz2.discourse-cdn.com/flex030/user_avatar/forums.fsharp.org/ntwilson/32/984_2.png) [@ntwilson](https://forums.fsharp.org/u/ntwilson)\
**Post date:** [January 29, 2024, 3:04pm UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/8 "2024-01-29T15:04:33Z")

</div>

Yeah `type private MyRecord { A : int }` and `type MyRecord private { A : int }` are different things, and I’m having trouble finding any documentation on the latter - I can only find StackOverflow posts talking about it, such as [this one](https://stackoverflow.com/a/12389894). The former makes the whole type private, so for example, you couldn’t even reference `MyRecord` outside of the same module/namespace. The latter only makes the constructor and the fields private, so you can still reference `MyRecord` from elsewhere, but you can’t make a new one and really do anything with an existing one unless it has some public members declared on it.

---

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [January 30, 2024, 9:01am UTC](https://forums.fsharp.org/t/is-there-a-way-to-hide-temporarily-unwanted-and-potentially-dangerous-values-in-a-record/3517/9 "2024-01-30T09:01:34Z")

</div>

Ah, now I get it.

After reading your explanations again I finally see what you’ve been trying to tell me all along.

I vaguely remembered something similar and then looked back into Scott Wlaschin’s book on Domain Modelling and found it there, in the chapter on Domain Integrity regarding the definition and validation of Simple Value types.

I’d read the book a few times but, because there are so many new (to me) things to learn in there, I just forgot about that particular ‘wrinkle’.

Now I understand (up to a point) what’s happening here, and I can also see where I can use it in other places.

Thanks again; your help is much appreciated.
