Repository: voxel51/fiftyone
Issue: [BUG] Copying views is broken for stages with `VALUE`

I'm having difficulty understanding the logic of `ViewExpression.__deepcopy__` but it is where the issue is, I believe.

```py
from copy import copy

import fiftyone as fo
import fiftyone.zoo as foz
from fiftyone.core.expressions import VALUE

F = fo.ViewField

dataset = foz.load_zoo_dataset("quickstart")
view = dataset.set_field("ground_truth.tags", F("$ground_truth.detections").reduce(VALUE.extend(F("tags")), []))
print(view._stages[-1]._expr)

copied_view = copy(view)
print(copied_view._stages[-1]._expr)
```

OUT:
```
{
    '$reduce': {
        'input': '$ground_truth.detections',
        'initialValue': [],
        'in': {'$concatArrays': ['$$value', '$$this.tags']},
    },
}
{
    '$reduce': {
        'input': '$ground_truth.detections',
        'initialValue': [],
        'in': {'$concatArrays': ['$value', '$tags']},
    },
}

```

The copied `expr` has one of its `$` removed.

Changed files:
fiftyone/core/expressions.py, tests/unittests/view_tests.py, fiftyone/__public__.py

diff --git a/fiftyone/__public__.py b/fiftyone/__public__.py
index 077283b88..029bceb9e 100644
--- a/fiftyone/__public__.py
+++ b/fiftyone/__public__.py
@@ -40,6 +40,7 @@ from .core.dataset import (
 from .core.expressions import (
     ViewField,
     ViewExpression,
+    VALUE,
 )
 from .core.fields import (
     ArrayField,
diff --git a/fiftyone/core/expressions.py b/fiftyone/core/expressions.py
index 070cc3c97..9c05b1b87 100644
--- a/fiftyone/core/expressions.py
+++ b/fiftyone/core/expressions.py
@@ -149,7 +149,7 @@ class ViewExpression(object):
 
     def __deepcopy__(self, memo):
         obj = self.__class__(deepcopy(self._expr, memo))
-        obj._prefix = deepcopy(self._prefix)
+        obj._prefix = deepcopy(self._prefix, memo)
         return obj
 
     def _freeze_prefix(self, prefix):
@@ -3406,6 +3406,12 @@ class ViewField(ViewExpression):
         if should_freeze:
             self._freeze_prefix("")
 
+    def __deepcopy__(self, memo):
+        obj = self.__class__()
+        obj._expr = deepcopy(self._expr, memo)
+        obj._prefix = deepcopy(self._prefix, memo)
+        return obj
+
     def to_mongo(self, prefix=None):
         """Returns a MongoDB representation of the field.
 
diff --git a/tests/unittests/view_tests.py b/tests/unittests/view_tests.py
index 4723b830f..dd926ae92 100644
--- a/tests/unittests/view_tests.py
+++ b/tests/unittests/view_tests.py
@@ -5,11 +5,12 @@ FiftyOne view-related unit tests.
 | `voxel51.com <https://voxel51.com/>`_
 |
 """
+from copy import deepcopy
 import math
 import unittest
 
 import fiftyone as fo
-from fiftyone import ViewField as F
+from fiftyone import ViewField as F, VALUE
 import fiftyone.core.sample as fos
 import fiftyone.core.stages as fosg
 
@@ -1012,6 +1013,12 @@ class ViewStageTests(unittest.TestCase):
             stage_dict["_uuid"], fosg.ViewStage._from_dict(stage_dict)._uuid
         )
 
+    def test_view_field_copy(self):
+        self.assertEqual(str(VALUE), str(deepcopy(VALUE)))
+
+        field = F("$ground_truth")
+        self.assertEqual(str(field), str(deepcopy(field)))
+
 
 if __name__ == "__main__":
     fo.config.show_progress_bars = False