diff --git a/src/polymesh/properties.hh b/src/polymesh/properties.hh
index ce977a94a21f98e7987c9d93790e56496f3476f2..34ebbc900c37b4303615e9b8080aa9a33c26bd54 100644
--- a/src/polymesh/properties.hh
+++ b/src/polymesh/properties.hh
@@ -1,11 +1,38 @@
 #pragma once
 #include "primitives.hh"
+#include "ranges.hh"
 #include "tmp.hh"
 
 namespace polymesh
 {
+template <class IteratorT, class FuncT>
+struct readonly_property_iterator
+{
+    using input_t = typename std::decay<decltype(std::declval<IteratorT>().operator*())>::type;
+    using output_t = typename tmp::decayed_result_type_of<FuncT, input_t>;
+
+    output_t operator*() { return func(*original_iterator); }
+
+    readonly_property_iterator& operator++()
+    {
+        ++original_iterator;
+        return *this;
+    }
+
+    bool operator==(readonly_property_iterator const& rhs) const { return original_iterator == rhs.original_iterator; }
+    bool operator!=(readonly_property_iterator const& rhs) const { return original_iterator != rhs.original_iterator; }
+
+    IteratorT original_iterator;
+    FuncT const& func;
+};
+
 template <class CollectionT, class FuncT>
-struct readonly_property
+struct readonly_property : smart_range<                               //
+                               readonly_property<CollectionT, FuncT>, //
+                               tmp::decayed_result_type_of<           //
+                                   FuncT,                             //
+                                   decltype(std::declval<CollectionT>().operator[](typename std::decay<CollectionT>::type::index_t()))>>
+
 {
     readonly_property(CollectionT collection, FuncT func) : mCollection(collection), mFunc(func) {}
 
@@ -29,6 +56,10 @@ struct readonly_property
         return {*this, f};
     }
 
+    using base_iter_t = typename std::decay<decltype(std::declval<CollectionT>().begin())>::type;
+    readonly_property_iterator<base_iter_t, FuncT> begin() const { return {mCollection.begin(), mFunc}; }
+    readonly_property_iterator<base_iter_t, FuncT> end() const { return {mCollection.end(), mFunc}; }
+
 private:
     CollectionT mCollection;
     FuncT mFunc;